diff --git a/.semaphore/semaphore.yml b/.semaphore/semaphore.yml index 18a30f8229a..ea6e7f35c76 100644 --- a/.semaphore/semaphore.yml +++ b/.semaphore/semaphore.yml @@ -4,13 +4,10 @@ agent: machine: type: e1-standard-8 os_image: ubuntu1804 - -auto_cancel: # cancel running CI for older commits on same branch if new commits are added +auto_cancel: running: - when: "branch != 'master'" - + when: branch != 'master' blocks: - - name: Tests dependencies: [] execution_time_limit: @@ -28,53 +25,37 @@ blocks: - mkdir ~/git && checkout && mv ~/test-secrets.json ~/git/flowcrypt-browser/test/test-secrets.json - cd ~/git/flowcrypt-browser - npm install - - echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)" + - 'echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)"' - npm run-script pretest - - sudo sh -c "echo '209.250.232.81 cron.flowcrypt.com' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 fes.google.mock.flowcryptlocal.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 google.mock.flowcryptlocal.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 fes.standardsubdomainfes.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 standardsubdomainfes.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 fes.disablefesaccesstoken.test' >> /etc/hosts" - jobs: - - name: code quality commands: - npm run-script test_tslint - npm run-script test_eslint - npm run-script test_stylelint - npm run-script test_patterns - - name: internals commands: - npm run-script test_async_stack - npm run-script test_buf - - name: consumer mock - unit tests commands: - npm run-script test_local_unit_consumer - - name: consumer mock - standard test group commands: - npm -v - node -v - npm run-script test_ci_chrome_consumer - - name: consumer mock - flaky test group commands: - npm run-script test_ci_chrome_consumer_flaky - - name: enterprise mock - unit tests commands: - npm run-script test_local_unit_enterprise - - name: enterprise mock - standard test group commands: - npm run-script test_ci_chrome_enterprise - cd ./build && zip -r ~/chrome-enterprise.zip ./chrome-enterprise/* && cd ~ -# - if [ "$SEMAPHORE_GIT_BRANCH" = master ]; artifact push project chrome-enterprise.zip --force; fi - epilogue: on_fail: commands: @@ -86,7 +67,7 @@ blocks: - name: Live Gmail tests dependencies: [] run: - when: "branch = 'master' OR branch =~ 'live-test' OR branch =~ 'gmail-test'" + when: branch = 'master' OR branch =~ 'live-test' OR branch =~ 'gmail-test' execution_time_limit: minutes: 20 task: @@ -102,15 +83,17 @@ blocks: - npm install -g npm@8.11.0 && mkdir ~/git && checkout && mv ~/test-secrets.json ~/git/flowcrypt-browser/test/test-secrets.json - cd ~/git/flowcrypt-browser - npm install - - echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)" + - 'echo "NODE=$(node --version), NPM=$(npm --version), TSC=$( ./node_modules/typescript/bin/tsc --version)"' - npm run-script pretest - - sudo sh -c "echo '209.250.232.81 cron.flowcrypt.com' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 fes.google.mock.flowcryptlocal.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 google.mock.flowcryptlocal.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 fes.standardsubdomainfes.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 standardsubdomainfes.test' >> /etc/hosts" - - sudo sh -c "echo '127.0.0.1 fes.disablefesaccesstoken.test' >> /etc/hosts" jobs: - name: Live Gmail tests commands: - npm run-script test_ci_chrome_consumer_live_gmail + epilogue: + on_fail: + commands: + - | + if [ -f build/test/test/debugArtifacts/debugHtmlAttachment-0.html ]; then + echo "Uploading debug files as job artifacts..." + artifact push job build/test/test/debugArtifacts/debugHtmlAttachment-0.html + fi \ No newline at end of file diff --git a/extension/changelog.txt b/extension/changelog.txt index 303dccd0d9b..f054baea6f0 100644 --- a/extension/changelog.txt +++ b/extension/changelog.txt @@ -1,5 +1,9 @@
+version 8.4.0 on November 18, 2022: : Pwd msg prompt fix, add self-sig key + +version 8.3.9 on November 8, 2022: : Reply improvements + version 8.3.8 on September 22, 2022: Stop using legacy pub submit version 8.3.7 on September 20, 2022: Reauth fix, EKM removals, configurable pp session diff --git a/extension/chrome/elements/add_pubkey.ts b/extension/chrome/elements/add_pubkey.ts index 80d7b75f705..ab250daf39b 100644 --- a/extension/chrome/elements/add_pubkey.ts +++ b/extension/chrome/elements/add_pubkey.ts @@ -47,7 +47,7 @@ View.run(class AddPubkeyView extends View { Xss.sanitizeAppend('select.copy_from_email', ``); } this.fetchKeyUi.handleOnPaste($('.pubkey')); - $('.action_settings').click(this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/contacts.htm'))); + $('.action_settings').on('click', this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/contacts.htm'))); }; public setHandlers = () => { @@ -68,8 +68,8 @@ View.run(class AddPubkeyView extends View { } }); $('select.copy_from_email').change(this.setHandler((el) => this.copyFromEmailHandler(el))); - $('.action_ok').click(this.setHandler(() => this.submitHandler())); - $('.action_close').click(this.setHandler(() => this.closeDialog())); + $('.action_ok').on('click', this.setHandler(() => this.submitHandler())); + $('.action_close').on('click', this.setHandler(() => this.closeDialog())); }; private closeDialog = () => { diff --git a/extension/chrome/elements/attachment.ts b/extension/chrome/elements/attachment.ts index a032b321d85..80a7be65ccd 100644 --- a/extension/chrome/elements/attachment.ts +++ b/extension/chrome/elements/attachment.ts @@ -101,9 +101,9 @@ export class AttachmentDownloadView extends View { public setHandlers = () => { Ui.event.protect(); if (this.canClickOnAttachment) { - this.downloadButton.click(this.setHandlerPrevent('double', () => this.downloadButtonClickedHandler())); - this.downloadButton.click((e) => e.stopPropagation()); - $('body').click(async () => { + this.downloadButton.on('click', this.setHandlerPrevent('double', () => this.downloadButtonClickedHandler())); + this.downloadButton.on('click', (e) => e.stopPropagation()); + $('body').on('click', async () => { if ($('body').attr('id') !== 'attachment-preview' && !$('body').hasClass('error-occured')) { await this.previewAttachmentClickedHandler(); } @@ -285,7 +285,7 @@ export class AttachmentDownloadView extends View { .html(`
Failed to decrypt:
see error details
Downloading original…`) // xss-escaped .addClass('error-occured') .attr('title', ''); - $('.see-error-details').click(async () => { + $('.see-error-details').on('click', async () => { await this.previewAttachmentClickedHandler(true); }); const name = this.attachment.name; diff --git a/extension/chrome/elements/attachment_preview.ts b/extension/chrome/elements/attachment_preview.ts index 86f54662996..d46f02ebad5 100644 --- a/extension/chrome/elements/attachment_preview.ts +++ b/extension/chrome/elements/attachment_preview.ts @@ -57,12 +57,12 @@ View.run(class AttachmentPreviewView extends AttachmentDownloadView { $('.attachment-preview-unavailable').prepend('No preview available'); // xss-escaped $('#attachment-preview-download').appendTo('.attachment-preview-unavailable'); } - $('body').click((e) => { + $('body').on('click', (e) => { if (e.target === document.body || $('body').children().toArray().indexOf(e.target) !== -1) { BrowserMsg.send.closeDialog(this.parentTabId); } }); - $('#attachment-preview-download').css('display', 'flex').click((e) => { + $('#attachment-preview-download').css('display', 'flex').on('click', (e) => { e.stopPropagation(); Browser.saveToDownloads(attachmentForSave); }); diff --git a/extension/chrome/elements/backup.htm b/extension/chrome/elements/backup.htm index 259dd6c6d56..e08d26f1b86 100644 --- a/extension/chrome/elements/backup.htm +++ b/extension/chrome/elements/backup.htm @@ -16,7 +16,7 @@
This backup is protected by your pass phrase. Please make sure to note your pass phrase down or you may lose access to your encrypted emails!
- +
Key Fingerprint:
diff --git a/extension/chrome/elements/backup.ts b/extension/chrome/elements/backup.ts index 4c68d4e4027..c1470cd694e 100644 --- a/extension/chrome/elements/backup.ts +++ b/extension/chrome/elements/backup.ts @@ -59,9 +59,9 @@ View.run(class BackupView extends View { public setHandlers = () => { if (!this.storedPrvWithMatchingLongid) { - $("#action_import_key").click(this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/add_key.htm'))); + $("#action_import_key").on('click', this.setHandler(async () => await Browser.openSettingsPage('index.htm', this.acctEmail, '/chrome/settings/modules/add_key.htm'))); } - $('.action_test_pass').click(this.setHandler(async () => this.testPassphraseHandler())); + $('.action_test_pass').on('click', this.setHandler(async () => this.testPassphraseHandler())); $('#pass_phrase').keydown(this.setEnterHandlerThatClicks('.action_test_pass')); }; diff --git a/extension/chrome/elements/compose-modules/compose-draft-module.ts b/extension/chrome/elements/compose-modules/compose-draft-module.ts index 91536904479..4ee78e4c3fc 100644 --- a/extension/chrome/elements/compose-modules/compose-draft-module.ts +++ b/extension/chrome/elements/compose-modules/compose-draft-module.ts @@ -45,7 +45,7 @@ export class ComposeDraftModule extends ViewModule { } public setHandlers = () => { - $('.delete_draft').click(this.view.setHandler(() => this.deleteDraftClickHandler(), this.view.errModule.handle('delete draft'))); + $('.delete_draft').on('click', this.view.setHandler(() => this.deleteDraftClickHandler(), this.view.errModule.handle('delete draft'))); this.view.recipientsModule.onRecipientAdded(async () => await this.draftSave(true)); }; @@ -363,10 +363,10 @@ export class ComposeDraftModule extends ViewModule { `).css({ display: 'flex', height: '100%' }); BrowserMsg.send.setActiveWindow(this.view.parentTabId, { frameId: this.view.frameId }); } - this.view.S.cached('prompt').find('.action_open_passphrase_dialog').click(this.view.setHandler(async () => { + this.view.S.cached('prompt').find('.action_open_passphrase_dialog').on('click', this.view.setHandler(async () => { BrowserMsg.send.passphraseDialog(this.view.parentTabId, { type: 'draft', longids }); })).focus(); - this.view.S.cached('prompt').find('.action_close').click(this.view.setHandler(() => this.view.renderModule.closeMsg())); + this.view.S.cached('prompt').find('.action_close').on('click', this.view.setHandler(() => this.view.renderModule.closeMsg())); const setActiveWindow = this.view.setHandler(async () => { BrowserMsg.send.setActiveWindow(this.view.parentTabId, { frameId: this.view.frameId }); }); this.view.S.cached('prompt').on('click', setActiveWindow).trigger('click'); await PassphraseStore.waitUntilPassphraseChanged(this.view.acctEmail, longids, 1000, this.view.ppChangedPromiseCancellation); diff --git a/extension/chrome/elements/compose-modules/compose-err-module.ts b/extension/chrome/elements/compose-modules/compose-err-module.ts index 6731b3ac864..4d51f524732 100644 --- a/extension/chrome/elements/compose-modules/compose-err-module.ts +++ b/extension/chrome/elements/compose-modules/compose-err-module.ts @@ -81,7 +81,7 @@ export class ComposeErrModule extends ViewModule { await Ui.modal.error(netErrMsg, true); } else if (ApiErr.isAuthErr(e)) { BrowserMsg.send.notificationShowAuthPopupNeeded(this.view.parentTabId, { acctEmail: this.view.acctEmail }); - Settings.offerToLoginWithPopupShowModalOnErr(this.view.acctEmail); + Settings.offerToLoginWithPopupShowModalOnErr(this.view.acctEmail, () => this.view.sendBtnModule.extractProcessSendMsg()); } else if (ApiErr.isReqTooLarge(e)) { await Ui.modal.error(`Could not send: message or attachments too large.`); } else if (ApiErr.isBadReq(e)) { @@ -151,7 +151,11 @@ export class ComposeErrModule extends ViewModule { } }; - public throwIfEncryptionPasswordInvalid = async ({ subject, pwd }: { subject: string, pwd?: string }) => { + public throwIfEncryptionPasswordInvalidOrDisabled = async ({ subject, pwd }: { subject: string, pwd?: string }) => { + // When DISABLE_FLOWCRYPT_HOSTED_PASSWORD_MESSAGES present, and recipients are missing a public key, and the user is using flowcrypt.com/api (not FES) + if (this.view.clientConfiguration.shouldDisablePasswordMessages() && !this.view.isFesUsed()) { + throw new ComposerUserError(Lang.compose.addMissingRecipientPubkeys); + } if (pwd) { if (await this.view.storageModule.isPwdMatchingPassphrase(pwd)) { throw new ComposerUserError('Please do not use your private key pass phrase as a password for this message.\n\n' + diff --git a/extension/chrome/elements/compose-modules/compose-input-module.ts b/extension/chrome/elements/compose-modules/compose-input-module.ts index 1b7fd55fb8b..3a409752845 100644 --- a/extension/chrome/elements/compose-modules/compose-input-module.ts +++ b/extension/chrome/elements/compose-modules/compose-input-module.ts @@ -17,7 +17,7 @@ export class ComposeInputModule extends ViewModule { public squire = new window.Squire(this.view.S.cached('input_text').get(0)); public setHandlers = () => { - this.view.S.cached('add_intro').click(this.view.setHandler(el => this.actionAddIntroHandler(el), this.view.errModule.handle(`add intro`))); + this.view.S.cached('add_intro').on('click', this.view.setHandler(el => this.actionAddIntroHandler(el), this.view.errModule.handle(`add intro`))); this.handlePaste(); this.handlePasteImages(); this.initShortcuts(); diff --git a/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts b/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts index d5c426f4c16..4ec67842596 100644 --- a/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts +++ b/extension/chrome/elements/compose-modules/compose-my-pubkey-module.ts @@ -18,7 +18,7 @@ export class ComposeMyPubkeyModule extends ViewModule { public setHandlers = () => { this.view.S.cached('icon_pubkey').attr('title', Lang.compose.includePubkeyIconTitle); - this.view.S.cached('icon_pubkey').click(this.view.setHandler((el) => this.iconPubkeyClickHandler(el), this.view.errModule.handle(`set/unset pub attachment`))); + this.view.S.cached('icon_pubkey').on('click', this.view.setHandler((el) => this.iconPubkeyClickHandler(el), this.view.errModule.handle(`set/unset pub attachment`))); }; public iconPubkeyClickHandler = (target: HTMLElement) => { diff --git a/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts b/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts index a26774e73ac..fbdded24ac8 100644 --- a/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts +++ b/extension/chrome/elements/compose-modules/compose-pwd-or-pubkey-container-module.ts @@ -24,27 +24,27 @@ export class ComposePwdOrPubkeyContainerModule extends ViewModule { } public setHandlers = () => { - this.view.S.cached('input_password').keyup(this.view.setHandlerPrevent('spree', () => this.showHideContainerAndColorSendBtn())); - this.view.S.cached('input_password').focus(this.view.setHandlerPrevent('spree', () => this.inputPwdFocusHandler())); - this.view.S.cached('input_password').blur(this.view.setHandler(() => this.inputPwdBlurHandler())); - this.view.S.cached('expiration_note').find('#expiration_note_settings_link').click(this.view.setHandler(async (el, e) => { - e.preventDefault(); - await this.view.renderModule.openSettingsWithDialog('security'); - }, this.view.errModule.handle(`render settings dialog`))); + this.view.S.cached('input_password').on('focus', this.view.setHandlerPrevent('spree', () => this.inputPwdFocusHandler())); + this.view.S.cached('input_password').on('blur', this.view.setHandler(() => this.inputPwdBlurHandler())); + this.view.S.cached('expiration_note').find('#expiration_note_settings_link').on( + 'click', + this.view.setHandler(async (el, e) => { + e.preventDefault(); + await this.view.renderModule.openSettingsWithDialog('security'); + }, this.view.errModule.handle(`render settings dialog`)) + ); }; public inputPwdFocusHandler = () => { const passwordContainerHeight = this.view.S.cached('password_or_pubkey').outerHeight() || 0; this.view.S.cached('expiration_note').css({ bottom: passwordContainerHeight }); this.view.S.cached('expiration_note').fadeIn(); - this.showHideContainerAndColorSendBtn(); // tslint:disable-line:no-floating-promises }; public inputPwdBlurHandler = () => { Catch.setHandledTimeout(() => { // timeout here is needed so will be visible once clicked this.view.S.cached('expiration_note').fadeOut(); }, 100); - this.showHideContainerAndColorSendBtn(); // tslint:disable-line:no-floating-promises }; public showHideContainerAndColorSendBtn = async () => { @@ -99,35 +99,34 @@ export class ComposePwdOrPubkeyContainerModule extends ViewModule { return true; }; + private initExpirationText = async () => { + // Init expiration text element + const expirationTextEl = this.view.S.cached('expiration_note').find('#expiration_note_message_expire'); + const pwdPolicy = this.view.fesUrl ? Lang.compose.enterprisePasswordPolicy : Lang.compose.consumerPasswordPolicy; + $('#password-policy-container').html(Xss.htmlSanitize(pwdPolicy.split('\n').join('
'))); // xss-sanitized + if (!this.view.acctEmail) { + expirationTextEl.text(Str.pluralize(this.MSG_EXPIRE_DAYS_DEFAULT, 'day')); + } else { + try { + const response = await this.view.acctServer.accountGetAndUpdateLocalStore(); + expirationTextEl.text(Str.pluralize(response.account.default_message_expire, 'day')); + } catch (e) { + ApiErr.reportIfSignificant(e); + expirationTextEl.text(`(unknown days: ${ApiErr.eli5(e)})`); + } + } + }; + private showMsgPwdUiAndColorBtn = async (anyNopgp: boolean, anyRevoked: boolean) => { + const isPasswordMessageDisabled = this.view.clientConfiguration.shouldDisablePasswordMessages() && !this.view.isFesUsed(); if (!this.isVisible()) { - const expirationTextEl = this.view.S.cached('expiration_note').find('#expiration_note_message_expire'); - const pwdPolicy = this.view.fesUrl ? Lang.compose.enterprisePasswordPolicy : Lang.compose.consumerPasswordPolicy; - $('#password-policy-container').html(Xss.htmlSanitize(pwdPolicy.split('\n').join('
'))); // xss-sanitized - if (!this.view.acctEmail) { - expirationTextEl.text(Str.pluralize(this.MSG_EXPIRE_DAYS_DEFAULT, 'day')); - } else { - try { - const response = await this.view.acctServer.accountGetAndUpdateLocalStore(); - expirationTextEl.text(Str.pluralize(response.account.default_message_expire, 'day')); - } catch (e) { - ApiErr.reportIfSignificant(e); - expirationTextEl.text(`(unknown days: ${ApiErr.eli5(e)})`); - } - } + await this.initExpirationText(); this.view.S.cached('password_or_pubkey').css('display', 'table-row'); } - if (this.view.S.cached('input_password').val() || this.view.S.cached('input_password').is(':focus')) { - this.view.S.cached('password_label').css('display', 'inline-block'); - this.view.S.cached('input_password').attr('placeholder', ''); - } else { - this.view.S.cached('password_label').css('display', 'none'); - this.view.S.cached('input_password').attr('placeholder', 'message password'); - } - if (this.view.S.cached('input_intro').is(':visible')) { - this.view.S.cached('add_intro').css('display', 'none'); + if (isPasswordMessageDisabled) { + this.view.S.cached('password_input_container').hide(); } else { - this.view.S.cached('add_intro').css('display', 'block'); + this.view.S.cached('add_intro').show(); } this.view.S.cached('warning_nopgp').css('display', anyNopgp ? 'inline-block' : 'none'); this.view.S.cached('warning_revoked').css('display', anyRevoked ? 'inline-block' : 'none'); @@ -135,11 +134,11 @@ export class ComposePwdOrPubkeyContainerModule extends ViewModule { }; private hideMsgPwdUi = () => { - this.view.S.cached('password_or_pubkey').css('display', 'none'); + this.view.S.cached('password_or_pubkey').hide(); this.view.S.cached('input_password').val(''); - this.view.S.cached('add_intro').css('display', 'none'); + this.view.S.cached('add_intro').hide(); this.view.S.cached('input_intro').text(''); - this.view.S.cached('intro_container').css('display', 'none'); + this.view.S.cached('intro_container').hide(); this.view.sizeModule.setInputTextHeightManuallyIfNeeded(); }; diff --git a/extension/chrome/elements/compose-modules/compose-quote-module.ts b/extension/chrome/elements/compose-modules/compose-quote-module.ts index 3caf8f33d6e..5347769946a 100644 --- a/extension/chrome/elements/compose-modules/compose-quote-module.ts +++ b/extension/chrome/elements/compose-modules/compose-quote-module.ts @@ -38,7 +38,7 @@ export class ComposeQuoteModule extends ViewModule { } const sanitizedFooter = textFooter && !this.view.draftModule.wasMsgLoadedFromDraft ? this.view.footerModule.createFooterHtml(textFooter) : undefined; this.tripleDotSanitizedHtmlContent = { footer: sanitizedFooter, quote: undefined }; - this.view.S.cached('triple_dot').click(this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el))); + this.view.S.cached('triple_dot').on('click', this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el))); }; public addTripleDotQuoteExpandFooterAndQuoteBtn = async (msgId: string, method: 'reply' | 'forward') => { @@ -77,7 +77,7 @@ export class ComposeQuoteModule extends ViewModule { if (method === 'forward') { this.actionRenderTripleDotContentHandle(this.view.S.cached('triple_dot')[0]); } else { - this.view.S.cached('triple_dot').click(this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el))); + this.view.S.cached('triple_dot').on('click', this.view.setHandler(el => this.actionRenderTripleDotContentHandle(el))); } }; diff --git a/extension/chrome/elements/compose-modules/compose-recipients-module.ts b/extension/chrome/elements/compose-modules/compose-recipients-module.ts index e00360aad57..d16f14fee73 100644 --- a/extension/chrome/elements/compose-modules/compose-recipients-module.ts +++ b/extension/chrome/elements/compose-modules/compose-recipients-module.ts @@ -42,11 +42,11 @@ export class ComposeRecipientsModule extends ViewModule { private dragged: Element | undefined = undefined; - private googleContactsSearchEnabled: boolean; + private googleContactsSearchEnabled: boolean | Promise; constructor(view: ComposeView) { super(view); - this.googleContactsSearchEnabled = this.view.scopes.readContacts && this.view.scopes.readOtherContacts; + this.googleContactsSearchEnabled = this.queryIfGoogleSearchEnabled(); } public setHandlers = (): void => { @@ -66,22 +66,22 @@ export class ComposeRecipientsModule extends ViewModule { inputs.on('dragover', (e) => e.preventDefault()); inputs.on('drop', this.view.setHandler((target) => this.inputsDropHandler(target))); this.view.S.cached('recipients_toggle_elements').on('focus', this.view.setHandler(() => this.collapseInputsIfNeeded())); - this.view.S.now('cc').click(this.view.setHandler((target) => { + this.view.S.now('cc').on('click', this.view.setHandler((target) => { const newContainer = this.view.S.cached('input_addresses_container_outer').find(`#input-container-cc`); this.copyCcBccActionsClickHandler(target, newContainer); })); - this.view.S.now('bcc').click(this.view.setHandler((target) => { + this.view.S.now('bcc').on('click', this.view.setHandler((target) => { const newContainer = this.view.S.cached('input_addresses_container_outer').find(`#input-container-bcc`); this.copyCcBccActionsClickHandler(target, newContainer); })); - this.view.S.cached('recipients_placeholder').click(this.view.setHandler(() => { + this.view.S.cached('recipients_placeholder').on('click', this.view.setHandler(() => { this.view.S.cached('input_to').focus(); })); this.view.S.cached('input_to').focus(this.view.setHandler(() => this.focusRecipients())); this.view.S.cached('cc').focus(this.view.setHandler(() => this.focusRecipients())); this.view.S.cached('bcc').focus(this.view.setHandler(() => this.focusRecipients())); - this.view.S.cached('compose_table').click(this.view.setHandler(() => this.hideContacts(), this.view.errModule.handle(`hide contact box`))); - this.view.S.cached('add_their_pubkey').click(this.view.setHandler(() => this.addTheirPubkeyClickHandler(), this.view.errModule.handle('add pubkey'))); + this.view.S.cached('compose_table').on('click', this.view.setHandler(() => this.hideContacts(), this.view.errModule.handle(`hide contact box`))); + this.view.S.cached('add_their_pubkey').on('click', this.view.setHandler(() => this.addTheirPubkeyClickHandler(), this.view.errModule.handle('add pubkey'))); BrowserMsg.addListener('addToContacts', this.checkReciepientsKeys); BrowserMsg.addListener('reRenderRecipient', async ({ email }: Bm.ReRenderRecipient) => { await this.reRenderRecipientFor(email); @@ -383,6 +383,16 @@ export class ComposeRecipientsModule extends ViewModule { this.setEmailsPreview(); }; + private queryIfGoogleSearchEnabled = async () => { + try { + const scopes = await AcctStore.getScopes(this.view.acctEmail); + return scopes.readContacts && scopes.readOtherContacts; + } catch (e) { + this.view.errModule.debug(`googleContactsSearchEnabled: Error occurred while fetching result: ${e}`); + return undefined; + } + }; + private inputsBlurHandler = async (target: HTMLElement, e: JQuery.Event) => { if (this.dragged) { // blur while drag&drop return; @@ -493,7 +503,7 @@ export class ComposeRecipientsModule extends ViewModule { } } else if (e.key === 'Enter') { if (currentActive.length) { // If he pressed enter when contacts popover is shown - currentActive.click(); // select contact + currentActive.trigger('click'); // select contact currentActive.removeClass('active'); } else { // We need to force add recipient even it's invalid this.parseRenderRecipients($(e.target), true).catch(Catch.reportErr); @@ -513,7 +523,7 @@ export class ComposeRecipientsModule extends ViewModule { } else if (e.key === 'Tab') { e.preventDefault(); // don't switch inputs e.stopPropagation(); // don't switch inputs - currentActive.click(); // select contact + currentActive.trigger('click'); // select contact currentActive.removeClass('active'); return true; } else if (e.key === 'ArrowUp') { @@ -555,19 +565,19 @@ export class ComposeRecipientsModule extends ViewModule { const contacts: ContactPreview[] = await ContactStore.search(undefined, { substring }); this.view.errModule.debug(`searchContacts substring: ${substring}`); this.view.errModule.debug(`searchContacts db count: ${contacts.length}`); - this.renderSearchRes(input, contacts, { substring }); + await this.renderSearchRes(input, contacts, { substring }); if (contacts.length >= this.MAX_CONTACTS_LENGTH) { this.view.errModule.debug(`searchContacts 2, count: ${contacts.length}`); return; } let foundOnGoogle: EmailProviderContact[] = []; - if (this.googleContactsSearchEnabled) { + if ((await this.googleContactsSearchEnabled) !== false) { this.view.errModule.debug(`searchContacts 3`); foundOnGoogle = await this.searchContactsOnGoogle(substring, contacts); await this.addApiLoadedContactsToDb(foundOnGoogle); this.view.errModule.debug(`searchContacts foundOnGoogle, count: ${foundOnGoogle.length}`); contacts.push(...foundOnGoogle.map(c => ContactStore.previewObj({ email: c.email, name: c.name }))); - this.renderSearchRes(input, contacts, { substring }); + await this.renderSearchRes(input, contacts, { substring }); if (contacts.length >= this.MAX_CONTACTS_LENGTH) { this.view.errModule.debug(`searchContacts 3.b, count: ${contacts.length}`); return; @@ -580,7 +590,7 @@ export class ComposeRecipientsModule extends ViewModule { await this.addApiLoadedContactsToDb(guessed.new); this.view.errModule.debug(`searchContacts (Gmail Sent Messages), count: ${guessed.new.length}`); contacts.push(...guessed.new.map(c => ContactStore.previewObj({ email: c.email, name: c.name }))); - this.renderSearchRes(input, contacts, { substring }); + await this.renderSearchRes(input, contacts, { substring }); }); } } catch (e) { @@ -610,12 +620,10 @@ export class ComposeRecipientsModule extends ViewModule { }; private searchContactsOnGoogle = async (query: string, knownContacts: ContactPreview[]): Promise => { - if (this.googleContactsSearchEnabled) { - this.view.errModule.debug(`searchContacts (Google API) 5`); - const contactsGoogle = await Google.contactsGet(this.view.acctEmail, query, undefined, this.MAX_CONTACTS_LENGTH); - if (contactsGoogle && contactsGoogle.length) { - return contactsGoogle.filter(cGmail => !knownContacts.find(c => c.email === cGmail.email)); - } + this.view.errModule.debug(`searchContacts (Google API) 5`); + const contactsGoogle = await Google.contactsGet(this.view.acctEmail, query, undefined, this.MAX_CONTACTS_LENGTH); + if (contactsGoogle && contactsGoogle.length) { + return contactsGoogle.filter(cGmail => !knownContacts.find(c => c.email === cGmail.email)); } return []; }; @@ -641,7 +649,7 @@ export class ComposeRecipientsModule extends ViewModule { }); }; - private renderSearchRes = (input: JQuery, contacts: ContactPreview[], query: ProviderContactsQuery) => { + private renderSearchRes = async (input: JQuery, contacts: ContactPreview[], query: ProviderContactsQuery) => { if (!input.is(':focus')) { // focus was moved away from input return; } @@ -695,7 +703,7 @@ export class ComposeRecipientsModule extends ViewModule { Xss.sanitizeRender(contactEl.find('ul'), ulHtml); const contactItems = contactEl.find('ul li.select_contact'); contactItems.first().addClass('active'); - contactItems.click(this.view.setHandlerPrevent('double', async (target: HTMLElement) => { + contactItems.on('click', this.view.setHandlerPrevent('double', async (target: HTMLElement) => { const email = Str.parseEmail($(target).attr('email') || '').email; if (email) { await this.selectContact(input, email, query); @@ -709,7 +717,7 @@ export class ComposeRecipientsModule extends ViewModule { } else { this.setContactPopupStyle(input); contactEl.find('ul').html('
  • No Contacts Found
  • '); // xss-direct - if (!this.googleContactsSearchEnabled) { + if ((await this.googleContactsSearchEnabled) === false) { this.addBtnToAllowSearchContactsFromGoogle(input); } } @@ -722,7 +730,7 @@ export class ComposeRecipientsModule extends ViewModule { this.view.S.cached('contacts') .append('') // xss-direct .find('.allow-google-contact-search') - .click(this.view.setHandler(async () => { + .on('click', this.view.setHandler(async () => { // Need to use BrowserMsg.send.bg because chrome.windows is undefined in gmail page const authResult = await BrowserMsg.send.bg.await.reconnectAcctAuthPopup({ acctEmail: this.view.acctEmail, scopes: GoogleAuth.defaultScopes('contacts') }); if (authResult.result === 'Success') { @@ -852,7 +860,7 @@ export class ComposeRecipientsModule extends ViewModule { `close`; Xss.sanitizeAppend(el, contentHtml) .find('img.close-icon') - .click(this.view.setHandler(target => this.removeRecipient(target.parentElement!), this.view.errModule.handle('remove recipient'))); + .on('click', this.view.setHandler(target => this.removeRecipient(target.parentElement!), this.view.errModule.handle('remove recipient'))); $(el).removeClass(['failed', 'wrong', 'has_pgp', 'no_pgp', 'expired']); if (recipient.status === RecipientStatus.WRONG) { this.view.errModule.debug(`renderPubkeyResult: Setting email to wrong / misspelled in harsh mode: ${recipient.invalid}`); @@ -873,8 +881,8 @@ export class ComposeRecipientsModule extends ViewModule { ` ); - $(el).find('.action_retry_pubkey_fetch').click(this.view.setHandler(async () => await this.refreshRecipients(), this.view.errModule.handle('refresh recipient'))); - $(el).find('.remove-reciepient').click(this.view.setHandler(element => this.removeRecipient(element.parentElement!), this.view.errModule.handle('remove recipient'))); + $(el).find('.action_retry_pubkey_fetch').on('click', this.view.setHandler(async () => await this.refreshRecipients(), this.view.errModule.handle('refresh recipient'))); + $(el).find('.remove-reciepient').on('click', this.view.setHandler(element => this.removeRecipient(element.parentElement!), this.view.errModule.handle('remove recipient'))); } else if (info && info.sortedPubkeys.length) { if (info.info.name) { recipient.name = info.info.name; diff --git a/extension/chrome/elements/compose-modules/compose-render-module.ts b/extension/chrome/elements/compose-modules/compose-render-module.ts index 1e0a989228b..6d13321f1df 100644 --- a/extension/chrome/elements/compose-modules/compose-render-module.ts +++ b/extension/chrome/elements/compose-modules/compose-render-module.ts @@ -53,7 +53,7 @@ export class ComposeRenderModule extends ViewModule { await this.renderReplyMsgComposeTable(); } else { $('#a_reply,#a_reply_all,#a_forward') - .click(this.view.setHandler((el) => this.actionActivateReplyBoxHandler(el), this.view.errModule.handle(`activate reply box`))); + .on('click', this.view.setHandler((el) => this.actionActivateReplyBoxHandler(el), this.view.errModule.handle(`activate reply box`))); } } } @@ -102,7 +102,7 @@ export class ComposeRenderModule extends ViewModule { } this.view.sizeModule.resizeComposeBox(); if (this.responseMethod === 'forward') { - this.view.S.cached('recipients_placeholder').click(); + this.view.S.cached('recipients_placeholder').trigger('click'); } BrowserMsg.send.scrollToReplyBox(this.view.parentTabId, { replyMsgId: `#${this.view.frameId}` }); }; @@ -285,10 +285,10 @@ export class ComposeRenderModule extends ViewModule { await this.view.recipientsModule.parseRenderRecipients(this.view.S.cached('input_to')); // this will force firefox to render them on load } } else { - $('.close_compose_window').click(this.view.setHandler(() => this.actionCloseHandler(), this.view.errModule.handle(`close compose window`))); - this.view.S.cached('title').click(() => { + $('.close_compose_window').on('click', this.view.setHandler(() => this.actionCloseHandler(), this.view.errModule.handle(`close compose window`))); + this.view.S.cached('title').on('click', () => { if (this.view.sizeModule.composeWindowIsMinimized) { - $('.minimize_compose_window').click(); + $('.minimize_compose_window').trigger('click'); } }); await this.view.quoteModule.addTripleDotQuoteExpandFooterOnlyBtn(); @@ -310,8 +310,8 @@ export class ComposeRenderModule extends ViewModule { this.view.S.cached('body').keydown(this.view.setHandler((el, ev) => this.onBodyKeydownHandler(el, ev))); this.view.S.cached('input_to').bind('paste', this.view.setHandler((el, ev) => this.onRecipientPasteHandler(el, ev))); this.view.inputModule.squire.addEventListener('input', () => this.view.S.cached('send_btn_note').text('')); - this.view.S.cached('input_addresses_container_inner').click(this.view.setHandler(() => this.onRecipientsClickHandler(), this.view.errModule.handle(`focus recipients`))); - this.view.S.cached('input_addresses_container_inner').children().click(() => false); + this.view.S.cached('input_addresses_container_inner').on('click', this.view.setHandler(() => this.onRecipientsClickHandler(), this.view.errModule.handle(`focus recipients`))); + this.view.S.cached('input_addresses_container_inner').children().on('click', () => false); this.view.S.cached('input_subject').bind('input', this.view.setHandler((el: HTMLInputElement) => this.subjectRTLHandler(el))).trigger('input'); }; @@ -366,7 +366,7 @@ export class ComposeRenderModule extends ViewModule { if (this.view.sizeModule.composeWindowIsMinimized) { return e.preventDefault(); } - Ui.escape(() => !this.view.isReplyBox && $('.close_compose_window').click())(e); + Ui.escape(() => !this.view.isReplyBox && $('.close_compose_window').trigger('click'))(e); const focusableEls = this.getFocusableEls(); const focusIndex = focusableEls.indexOf(e.target); if (focusIndex !== -1) { // Focus trap (Tab, Shift+Tab) diff --git a/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts b/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts index ab7eee4b7f2..4443517dd81 100644 --- a/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts +++ b/extension/chrome/elements/compose-modules/compose-reply-btn-popover-module.ts @@ -18,7 +18,7 @@ export class ComposeReplyBtnPopoverModule extends ViewModule { }; public setHandlers = (): void => { - this.view.S.cached('toggle_reply_options').click(this.view.setHandler((el, ev) => this.toggleVisible(ev))); + this.view.S.cached('toggle_reply_options').on('click', this.view.setHandler((el, ev) => this.toggleVisible(ev))); }; public render = async (isReply = true) => { @@ -32,7 +32,7 @@ export class ComposeReplyBtnPopoverModule extends ViewModule {
    ${Xss.escape(item.text)}
    `); - elem.click(this.view.setHandler(() => this.didOptionClick(option))); + elem.on('click', this.view.setHandler(() => this.didOptionClick(option))); elem.find('.option-name').prepend(``); // xss-direct this.view.S.cached('reply_options_container').append(elem); // xss-safe-factory } diff --git a/extension/chrome/elements/compose-modules/compose-send-btn-module.ts b/extension/chrome/elements/compose-modules/compose-send-btn-module.ts index dd512308e37..3ccef566420 100644 --- a/extension/chrome/elements/compose-modules/compose-send-btn-module.ts +++ b/extension/chrome/elements/compose-modules/compose-send-btn-module.ts @@ -39,7 +39,7 @@ export class ComposeSendBtnModule extends ViewModule { public setHandlers = (): void => { const ctrlEnterHandler = Ui.ctrlEnter(() => !this.view.sizeModule.composeWindowIsMinimized && this.extractProcessSendMsg()); this.view.S.cached('subject').add(this.view.S.cached('compose')).keydown(ctrlEnterHandler); - this.view.S.cached('send_btn').click(this.view.setHandlerPrevent('double', () => this.extractProcessSendMsg())); + this.view.S.cached('send_btn').on('click', this.view.setHandlerPrevent('double', () => this.extractProcessSendMsg())); this.popover.setHandlers(); }; @@ -86,17 +86,7 @@ export class ComposeSendBtnModule extends ViewModule { } }; - private btnText = (): string => { - if (this.popover.choices.encrypt && this.popover.choices.sign) { - return SendBtnTexts.BTN_ENCRYPT_SIGN_AND_SEND; - } else if (this.popover.choices.sign) { - return SendBtnTexts.BTN_SIGN_AND_SEND; - } else { - return SendBtnTexts.BTN_PLAIN_SEND; - } - }; - - private extractProcessSendMsg = async () => { + public extractProcessSendMsg = async () => { if (this.view.S.cached('reply_msg_successful').is(':visible')) { return; } @@ -125,7 +115,10 @@ export class ComposeSendBtnModule extends ViewModule { Ui.toast(result.supplementaryOperationsErrors[0] as string); }, 0); } - BrowserMsg.send.notificationShow(this.view.parentTabId, { notification: `Your ${this.view.isReplyBox ? 'reply' : 'message'} has been sent.` }); + BrowserMsg.send.notificationShow(this.view.parentTabId, { + notification: `Your ${this.view.isReplyBox ? 'reply' : 'message'} has been sent.`, + group: 'compose' + }); BrowserMsg.send.focusBody(this.view.parentTabId); // Bring focus back to body so Gmails shortcuts will work if (this.view.isReplyBox) { this.view.renderModule.renderReplySuccess(msgObj.renderSentMessage.attachments, msgObj.renderSentMessage.recipients, result.sentIds[0]); @@ -144,6 +137,16 @@ export class ComposeSendBtnModule extends ViewModule { } }; + private btnText = (): string => { + if (this.popover.choices.encrypt && this.popover.choices.sign) { + return SendBtnTexts.BTN_ENCRYPT_SIGN_AND_SEND; + } else if (this.popover.choices.sign) { + return SendBtnTexts.BTN_SIGN_AND_SEND; + } else { + return SendBtnTexts.BTN_PLAIN_SEND; + } + }; + private finalizeSendableMsg = async ({ msg, senderKi }: { msg: SendableMsg, senderKi: KeyInfoWithIdentity | undefined }) => { const choices = this.view.sendBtnModule.popover.choices; for (const k of Object.keys(this.additionalMsgHeaders)) { diff --git a/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts b/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts index 14c58bc773f..2d7a5d06724 100644 --- a/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts +++ b/extension/chrome/elements/compose-modules/compose-send-btn-popover-module.ts @@ -16,7 +16,7 @@ export class ComposeSendBtnPopoverModule extends ViewModule { public choices: PopoverChoices = { encrypt: true, sign: true, richtext: false }; // defaults, may be changed by user using the popover public setHandlers = (): void => { - this.view.S.cached('toggle_send_options').click(this.view.setHandler((el, ev) => this.toggleVisible(ev))); + this.view.S.cached('toggle_send_options').on('click', this.view.setHandler((el, ev) => this.toggleVisible(ev))); }; public render = async () => { @@ -37,7 +37,7 @@ export class ComposeSendBtnPopoverModule extends ViewModule { ${Xss.escape(item.text)}
    `); this.renderCrossOrTick(elem, popoverOpt, this.choices[popoverOpt]); - elem.click(this.view.setHandler(() => this.toggleItemTick(elem, popoverOpt))); + elem.on('click', this.view.setHandler(() => this.toggleItemTick(elem, popoverOpt))); if (item.iconPath) { elem.find('.option-name').prepend(``); // xss-direct } @@ -138,7 +138,7 @@ export class ComposeSendBtnPopoverModule extends ViewModule { } else if (e.key === 'Enter') { e.stopPropagation(); e.preventDefault(); - currentActive.click(); + currentActive.trigger('click'); } }; diff --git a/extension/chrome/elements/compose-modules/compose-size-module.ts b/extension/chrome/elements/compose-modules/compose-size-module.ts index aeee7c2c8df..262d82f05ec 100644 --- a/extension/chrome/elements/compose-modules/compose-size-module.ts +++ b/extension/chrome/elements/compose-modules/compose-size-module.ts @@ -19,14 +19,14 @@ export class ComposeSizeModule extends ViewModule { private currentWindowSelector = `.secure_compose_window[data-frame-id="${this.view.frameId}"]`; public setHandlers = () => { - $('body').click(event => { + $('body').on('click', event => { if (this.composeWindowIsMaximized && $(event.target).is($('body'))) { this.minimizeComposerWindow(); } }); if (!this.view.isReplyBox) { - $('.minimize_compose_window').click(this.view.setHandler(() => this.minimizeComposerWindow())); - $('.popout').click(this.view.setHandler(() => this.popoutClickHandler())); + $('.minimize_compose_window').on('click', this.view.setHandler(() => this.minimizeComposerWindow())); + $('.popout').on('click', this.view.setHandler(() => this.popoutClickHandler())); } }; diff --git a/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts b/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts index 1bb623b7f0b..7f8631e873b 100644 --- a/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts +++ b/extension/chrome/elements/compose-modules/formatters/encrypted-mail-msg-formatter.ts @@ -243,7 +243,7 @@ export class EncryptedMsgMailFormatter extends BaseMailFormatter { }; } catch (msgTokenErr) { if (ApiErr.isAuthErr(msgTokenErr)) { - Settings.offerToLoginWithPopupShowModalOnErr(this.acctEmail); + Settings.offerToLoginWithPopupShowModalOnErr(this.acctEmail, () => this.view.sendBtnModule.extractProcessSendMsg()); throw new ComposerResetBtnTrigger(); } else if (ApiErr.isNetErr(msgTokenErr)) { throw msgTokenErr; diff --git a/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts b/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts index e4709850e44..5d9602c4a0e 100644 --- a/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts +++ b/extension/chrome/elements/compose-modules/formatters/general-mail-formatter.ts @@ -44,7 +44,7 @@ export class GeneralMailFormatter { // encrypt (optionally sign) const singleFamilyKeys = await view.storageModule.collectSingleFamilyKeys(recipientsEmails, newMsgData.from.email, choices.sign); if (singleFamilyKeys.emailsWithoutPubkeys.length) { - await view.errModule.throwIfEncryptionPasswordInvalid(newMsgData); + await view.errModule.throwIfEncryptionPasswordInvalidOrDisabled(newMsgData); } let signingKey: ParsedKeyInfo | undefined; if (choices.sign) { diff --git a/extension/chrome/elements/compose.htm b/extension/chrome/elements/compose.htm index 5c98392d84d..c73edb95170 100644 --- a/extension/chrome/elements/compose.htm +++ b/extension/chrome/elements/compose.htm @@ -60,13 +60,13 @@

    New Secure Message

    -
    -
    - +
    +
    +
    - -
    `; } Xss.sanitizeAppend('.key_list', html); - $('.action_show_key').click(this.setHandler(async target => { + $('.action_show_key').on('click', this.setHandler(async target => { // the UI below only gets rendered when account_email is available await Settings.renderSubPage(this.acctEmail!, this.tabId, $(target).attr('page')!, $(target).attr('addurltext') || ''); // all such elements do have page attr })); if (canRemoveKey) { - $('.action_remove_key').click(this.setHandler(async target => { + $('.action_remove_key').on('click', this.setHandler(async target => { // the UI below only gets rendered when account_email is available const family = $(target).data('type') as string; const id = $(target).data('id') as string; diff --git a/extension/chrome/settings/modules/add_key.htm b/extension/chrome/settings/modules/add_key.htm index 48e97fc1a69..943e45321d6 100644 --- a/extension/chrome/settings/modules/add_key.htm +++ b/extension/chrome/settings/modules/add_key.htm @@ -18,18 +18,29 @@
    -
    +

    Add Private Key

      -
    • -
    • -
    • +
    • + + +
    • +
    • + + +
    • +
    • + + +
    @@ -38,19 +49,23 @@

    Add Private Key

    - +
    - This key is unprotected. Create a pass phrase or use - a random one. + This key is unprotected. Create a pass phrase or + + use a random one + + .
    - +
    @@ -60,6 +75,8 @@

    Add Private Key

    +
    +
    diff --git a/extension/chrome/settings/modules/add_key.ts b/extension/chrome/settings/modules/add_key.ts index a0e1fcb0fc5..83fb1b18ea6 100644 --- a/extension/chrome/settings/modules/add_key.ts +++ b/extension/chrome/settings/modules/add_key.ts @@ -13,11 +13,12 @@ import { Ui } from '../../../js/common/browser/ui.js'; import { View } from '../../../js/common/view.js'; import { Xss } from '../../../js/common/platform/xss.js'; import { initPassphraseToggle } from '../../../js/common/ui/passphrase-ui.js'; -import { UnexpectedKeyTypeError } from '../../../js/common/core/crypto/key.js'; +import { Key, UnexpectedKeyTypeError } from '../../../js/common/core/crypto/key.js'; import { ClientConfiguration } from '../../../js/common/client-configuration.js'; import { Lang } from '../../../js/common/lang.js'; import { AcctStore } from '../../../js/common/platform/store/acct-store.js'; import { saveKeysAndPassPhrase, setPassphraseForPrvs } from '../../../js/common/helpers.js'; +import { Settings } from '../../../js/common/settings.js'; View.run(class AddKeyView extends View { @@ -65,10 +66,12 @@ View.run(class AddKeyView extends View { }; public setHandlers = () => { - $('.action_add_private_key').click(this.setHandlerPrevent('double', this.addPrivateKeyHandler)); + $('.action_add_private_key').on('click', this.setHandlerPrevent('double', this.addPrivateKeyHandler)); $('#input_passphrase').keydown(this.setEnterHandlerThatClicks('.action_add_private_key')); }; + private isFesUsed = () => Boolean(this.fesUrl); + private loadAndRenderKeyBackupsOrRenderError = async () => { try { const backups = await this.gmail.fetchKeyBackups(); @@ -90,6 +93,17 @@ View.run(class AddKeyView extends View { } }; + private saveKeyAndContinue = async (key: Key) => { + await saveKeysAndPassPhrase(this.acctEmail, [key]); // resulting new_key checked above + await setPassphraseForPrvs( + this.clientConfiguration, + this.acctEmail, + [key], + { passphrase: String($('.input_passphrase').val()), passphrase_save: !!$('.input_passphrase_save').prop('checked') } + ); + BrowserMsg.send.reload(this.parentTabId, { advanced: true }); + }; + private addPrivateKeyHandler = async (submitBtn: HTMLElement) => { if (submitBtn.className.includes('gray')) { await Ui.modal.warning('Please double check the pass phrase input field for any issues.'); @@ -98,28 +112,44 @@ View.run(class AddKeyView extends View { try { const checked = await this.keyImportUi.checkPrv(this.acctEmail, String($('.input_private_key').val()), String($('.input_passphrase').val())); if (checked) { - await saveKeysAndPassPhrase(this.acctEmail, [checked.encrypted]); // resulting new_key checked above - await setPassphraseForPrvs( - this.clientConfiguration, - this.acctEmail, - [checked.encrypted], - { passphrase: checked.passphrase, passphrase_save: !!$('.input_passphrase_save').prop('checked') } - ); - BrowserMsg.send.reload(this.parentTabId, { advanced: true }); + await this.saveKeyAndContinue(checked.encrypted); } } catch (e) { if (e instanceof UserAlert) { return await Ui.modal.warning(e.message, Ui.testCompatibilityLink); } else if (e instanceof KeyCanBeFixed) { - return await Ui.modal.error(`This type of key cannot be set as additional keys yet. ${Lang.general.contactForSupportSentence(!!this.fesUrl)}`, - false, Ui.testCompatibilityLink); + return await this.renderCompatibilityFixBlockAndFinalizeSetup(e.encrypted); } else if (e instanceof UnexpectedKeyTypeError) { return await Ui.modal.warning(`This does not appear to be a validly formatted key.\n\n${e.message}`); } else { Catch.reportErr(e); - return await Ui.modal.error(`An error happened when processing the key: ${String(e)}\n${Lang.general.contactForSupportSentence(!!this.fesUrl)}`, + return await Ui.modal.error(`An error happened when processing the key: ${String(e)}\n${Lang.general.contactForSupportSentence(this.isFesUsed())}`, false, Ui.testCompatibilityLink); } } }; + + private toggleCompatibilityView = (visible: boolean) => { + if (visible) { + $('#add_key_container').hide(); + $('#compatibility_fix').show(); + } else { + $('#add_key_container').show(); + $('#compatibility_fix').hide(); + } + }; + + private renderCompatibilityFixBlockAndFinalizeSetup = async (origPrv: Key) => { + let fixedPrv; + try { + this.toggleCompatibilityView(true); + fixedPrv = await Settings.renderPrvCompatFixUiAndWaitTilSubmittedByUser( + this.acctEmail, '#compatibility_fix', origPrv, String($('.input_passphrase').val()), window.location.href.replace(/#$/, '')); + await this.saveKeyAndContinue(fixedPrv); + } catch (e) { + Catch.reportErr(e); + await Ui.modal.error(`Failed to fix key (${String(e)}). ${Lang.general.writeMeToFixIt(this.isFesUsed())}`, false, Ui.testCompatibilityLink); + this.toggleCompatibilityView(false); + } + }; }); diff --git a/extension/chrome/settings/modules/change_passphrase.htm b/extension/chrome/settings/modules/change_passphrase.htm index d6d112619cb..1993c735e79 100644 --- a/extension/chrome/settings/modules/change_passphrase.htm +++ b/extension/chrome/settings/modules/change_passphrase.htm @@ -21,7 +21,7 @@
    Enter your current pass phrase
    - +
    diff --git a/extension/chrome/settings/modules/change_passphrase.ts b/extension/chrome/settings/modules/change_passphrase.ts index 4d8c4813df6..f49b8ae05e3 100644 --- a/extension/chrome/settings/modules/change_passphrase.ts +++ b/extension/chrome/settings/modules/change_passphrase.ts @@ -65,10 +65,10 @@ View.run(class ChangePassPhraseView extends View { }; public setHandlers = () => { - $('#step_0_enter_current .action_test_current_passphrase').click(this.setHandler(() => this.actionTestCurrentPassPhraseHandler())); - $('#step_1_enter_new .action_set_pass_phrase').click(this.setHandler(el => this.actionSetPassPhraseHandler(el))); - $('#step_2_confirm_new .action_use_another').click(this.setHandler(() => this.actionUseAnotherPassPhraseHandler())); - $('#step_2_confirm_new .action_change').click(this.setHandlerPrevent('double', () => this.actionDoChangePassPhraseHandler())); + $('#step_0_enter_current .action_test_current_passphrase').on('click', this.setHandler(() => this.actionTestCurrentPassPhraseHandler())); + $('#step_1_enter_new .action_set_pass_phrase').on('click', this.setHandler(el => this.actionSetPassPhraseHandler(el))); + $('#step_2_confirm_new .action_use_another').on('click', this.setHandler(() => this.actionUseAnotherPassPhraseHandler())); + $('#step_2_confirm_new .action_change').on('click', this.setHandlerPrevent('double', () => this.actionDoChangePassPhraseHandler())); $('#current_pass_phrase').on('keydown', this.setEnterHandlerThatClicks('#step_0_enter_current .action_test_current_passphrase')); $('#new_pass_phrase').on('keydown', this.setEnterHandlerThatClicks('#step_1_enter_new .action_set_pass_phrase')); $("#new_pass_phrase_confirm").on('keydown', this.setEnterHandlerThatClicks('#step_2_confirm_new .action_change')); diff --git a/extension/chrome/settings/modules/compatibility.htm b/extension/chrome/settings/modules/compatibility.htm index c7407e780ad..3bfd5ac5eac 100644 --- a/extension/chrome/settings/modules/compatibility.htm +++ b/extension/chrome/settings/modules/compatibility.htm @@ -24,7 +24,7 @@

    OpenPGP key compatibility test

    - +
    diff --git a/extension/chrome/settings/modules/compatibility.ts b/extension/chrome/settings/modules/compatibility.ts index 506ecfd7120..935ec931e13 100644 --- a/extension/chrome/settings/modules/compatibility.ts +++ b/extension/chrome/settings/modules/compatibility.ts @@ -20,7 +20,7 @@ View.run(class CompatibilityView extends View { }; public setHandlers = () => { - $('.action_test_key').click(this.setHandlerPrevent('double', this.actionTestKeyHandler)); + $('.action_test_key').on('click', this.setHandlerPrevent('double', this.actionTestKeyHandler)); $('#input_passphrase').keydown(this.setEnterHandlerThatClicks('.action_test_key')); }; diff --git a/extension/chrome/settings/modules/contacts.ts b/extension/chrome/settings/modules/contacts.ts index a4fb7af3125..3f90637ed56 100644 --- a/extension/chrome/settings/modules/contacts.ts +++ b/extension/chrome/settings/modules/contacts.ts @@ -53,11 +53,11 @@ View.run(class ContactsView extends View { }; public setHandlers = () => { - $('.action_show_pubkey_list').off().click(this.setHandlerPrevent('double', this.actionRenderListPublicKeyHandler)); - $('#edit_contact .action_save_edited_pubkey').off().click(this.setHandlerPrevent('double', this.actionSaveEditedPublicKeyHandler)); - $('#bulk_import .action_process').off().click(this.setHandlerPrevent('double', this.actionProcessBulkImportTextInput)); - $('.action_export_all').off().click(this.setHandlerPrevent('double', this.actionExportAllKeysHandler)); - $('.action_view_bulk_import').off().click(this.setHandlerPrevent('double', this.actionRenderBulkImportPageHandler)); + $('.action_show_pubkey_list').off().on('click', this.setHandlerPrevent('double', this.actionRenderListPublicKeyHandler)); + $('#edit_contact .action_save_edited_pubkey').off().on('click', this.setHandlerPrevent('double', this.actionSaveEditedPublicKeyHandler)); + $('#bulk_import .action_process').off().on('click', this.setHandlerPrevent('double', this.actionProcessBulkImportTextInput)); + $('.action_export_all').off().on('click', this.setHandlerPrevent('double', this.actionExportAllKeysHandler)); + $('.action_view_bulk_import').off().on('click', this.setHandlerPrevent('double', this.actionRenderBulkImportPageHandler)); $('.input-search-contacts').off().keyup(this.setHandlerPrevent('double', this.loadAndRenderContactList)); }; @@ -68,7 +68,8 @@ View.run(class ContactsView extends View { let lineActionsHtml = '  export all  ' + '  import public keys  '; if (this.clientConfiguration.getCustomSksPubkeyServer()) { - lineActionsHtml += `  

    using custom SKS pubkeyserver: ${Xss.escape(this.clientConfiguration!.getCustomSksPubkeyServer()!)}`; + lineActionsHtml += `  

    ` + + `using custom SKS pubkeyserver: ${Xss.escape(this.clientConfiguration!.getCustomSksPubkeyServer()!)}`; } else { lineActionsHtml += '  use custom keyserver  '; } @@ -144,9 +145,9 @@ View.run(class ContactsView extends View { // remove all listeners from the old link by creating a new element const newElement = emailRow.cloneNode(true); emailRow!.parentNode!.replaceChild(newElement, emailRow); - $('.action_remove').off().click(this.setHandlerPrevent('double', this.actionRemovePublicKey)); - $('.action_show').off().click(this.setHandlerPrevent('double', this.actionRenderViewPublicKeyHandler)); - $('.action_change').off().click(this.setHandlerPrevent('double', this.actionRenderChangePublicKeyHandler)); + $('.action_remove').off().on('click', this.setHandlerPrevent('double', this.actionRemovePublicKey)); + $('.action_show').off().on('click', this.setHandlerPrevent('double', this.actionRenderViewPublicKeyHandler)); + $('.action_change').off().on('click', this.setHandlerPrevent('double', this.actionRenderChangePublicKeyHandler)); } }; @@ -176,7 +177,7 @@ View.run(class ContactsView extends View { `Usable for signing: ${key.usableForSigning}`, ].join('\n')); $('#view_contact').css('display', 'block'); - $('#page_back_button').click(this.setHandler(() => this.loadAndRenderContactList())); + $('#page_back_button').on('click', this.setHandler(() => this.loadAndRenderContactList())); }; private actionRenderChangePublicKeyHandler = (changePubkeyButton: HTMLElement) => { @@ -185,7 +186,7 @@ View.run(class ContactsView extends View { Xss.sanitizeRender('h1', `${this.backBtn}${this.space}${Xss.escape(email)}${this.space}(edit)`); $('#edit_contact').css('display', 'block'); $('#edit_contact .input_pubkey').val('').attr('email', email); - $('#page_back_button').click(this.setHandler(() => this.loadAndRenderContactList())); + $('#page_back_button').on('click', this.setHandler(() => this.loadAndRenderContactList())); }; private actionSaveEditedPublicKeyHandler = async () => { @@ -224,7 +225,7 @@ View.run(class ContactsView extends View { $('#bulk_import #processed').text('').css('display', 'none'); $('#file_import').show(); $('#file_import #fineuploader_button').css('display', 'inline-block'); - $('#page_back_button').click(this.setHandler(() => this.loadAndRenderContactList())); + $('#page_back_button').on('click', this.setHandler(() => this.loadAndRenderContactList())); }; private actionProcessBulkImportTextInput = async () => { diff --git a/extension/chrome/settings/modules/debug_api.ts b/extension/chrome/settings/modules/debug_api.ts index 0ec20372cca..35e3f18776c 100644 --- a/extension/chrome/settings/modules/debug_api.ts +++ b/extension/chrome/settings/modules/debug_api.ts @@ -35,7 +35,7 @@ View.run(class DebugApiView extends View { Xss.sanitizeAppend('#content', `Unsupported which: ${Xss.escape(this.which)} (not implemented)`); } else if (this.which === 'local_store') { const storage = await AcctStore.get(this.acctEmail, [ - 'notification_setup_needed_dismissed', 'email_provider', 'google_token_scopes', 'hide_message_password', 'sendAs', 'outgoing_language', + 'notification_setup_needed_dismissed', 'email_provider', 'hide_message_password', 'sendAs', 'outgoing_language', 'full_name', 'cryptup_enabled', 'setup_done', 'successfully_received_at_leat_one_message', 'notification_setup_done_seen', 'rules', 'use_rich_text', 'fesUrl' diff --git a/extension/chrome/settings/modules/decrypt.ts b/extension/chrome/settings/modules/decrypt.ts index 4a0e47c61d3..fb45d7fa764 100644 --- a/extension/chrome/settings/modules/decrypt.ts +++ b/extension/chrome/settings/modules/decrypt.ts @@ -38,7 +38,7 @@ View.run(class ManualDecryptView extends View { }; public setHandlers = () => { - $('.action_decrypt_and_download').click(this.setHandlerPrevent('double', el => this.actionDecryptAndDownloadHandler(el))); + $('.action_decrypt_and_download').on('click', this.setHandlerPrevent('double', el => this.actionDecryptAndDownloadHandler(el))); }; private actionDecryptAndDownloadHandler = async (button: HTMLElement) => { diff --git a/extension/chrome/settings/modules/experimental.ts b/extension/chrome/settings/modules/experimental.ts index d66c4fe253c..315b4f7ca8d 100644 --- a/extension/chrome/settings/modules/experimental.ts +++ b/extension/chrome/settings/modules/experimental.ts @@ -40,15 +40,15 @@ View.run(class ExperimentalView extends View { }; public setHandlers = () => { - $('.action_open_compatibility').click(this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/compatibility.htm'))); - $('.action_open_decrypt').click(this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/decrypt.htm'))); - $('.action_throw_unchecked').click((e) => { e.preventDefault(); Catch.test('error'); }); - $('.action_throw_err').click(this.setHandler((el, e) => { e.preventDefault(); Catch.test('error'); })); - $('.action_throw_obj').click(this.setHandler((el, e) => { e.preventDefault(); Catch.test('object'); })); - $('.action_reset_account').click(this.setHandler(async (el, e) => { e.preventDefault(); await this.acctResetHandler(); })); - $('.action_make_google_auth_token_unusable').click(this.setHandler(async (el, e) => { e.preventDefault(); await this.makeGoogleAuthTokenUnusableHandler(); })); - $('.action_make_google_refresh_token_unusable').click(this.setHandler(async (el, e) => { e.preventDefault(); await this.makeGoogleRefreshTokenUnusableHandler(); })); - $('.action_account_email_changed').click(this.setHandler(async (el, e) => { e.preventDefault(); await this.acctEmailChangedHandler(); })); + $('.action_open_compatibility').on('click', this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/compatibility.htm'))); + $('.action_open_decrypt').on('click', this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/decrypt.htm'))); + $('.action_throw_unchecked').on('click', (e) => { e.preventDefault(); Catch.test('error'); }); + $('.action_throw_err').on('click', this.setHandler((el, e) => { e.preventDefault(); Catch.test('error'); })); + $('.action_throw_obj').on('click', this.setHandler((el, e) => { e.preventDefault(); Catch.test('object'); })); + $('.action_reset_account').on('click', this.setHandler(async (el, e) => { e.preventDefault(); await this.acctResetHandler(); })); + $('.action_make_google_auth_token_unusable').on('click', this.setHandler(async (el, e) => { e.preventDefault(); await this.makeGoogleAuthTokenUnusableHandler(); })); + $('.action_make_google_refresh_token_unusable').on('click', this.setHandler(async (el, e) => { e.preventDefault(); await this.makeGoogleRefreshTokenUnusableHandler(); })); + $('.action_account_email_changed').on('click', this.setHandler(async (el, e) => { e.preventDefault(); await this.acctEmailChangedHandler(); })); }; // -- PRIVATE diff --git a/extension/chrome/settings/modules/help.htm b/extension/chrome/settings/modules/help.htm index 39272cf47c0..c716b79913f 100644 --- a/extension/chrome/settings/modules/help.htm +++ b/extension/chrome/settings/modules/help.htm @@ -22,7 +22,7 @@

    Send message to FlowCrypt developers


    - +
    diff --git a/extension/chrome/settings/modules/help.ts b/extension/chrome/settings/modules/help.ts index 6183b566f3d..279e85370b8 100644 --- a/extension/chrome/settings/modules/help.ts +++ b/extension/chrome/settings/modules/help.ts @@ -42,7 +42,7 @@ View.run(class HelpView extends View { }; public setHandlers = () => { - $('.action_send_feedback').click(this.setHandler(el => this.sendFeedbackHandler(el))); + $('.action_send_feedback').on('click', this.setHandler(el => this.sendFeedbackHandler(el))); }; // --- PRIVATE diff --git a/extension/chrome/settings/modules/keyserver.ts b/extension/chrome/settings/modules/keyserver.ts index 68fa66eba37..770a3b02d20 100644 --- a/extension/chrome/settings/modules/keyserver.ts +++ b/extension/chrome/settings/modules/keyserver.ts @@ -80,8 +80,8 @@ View.run(class KeyserverView extends View { }; public setHandlers = () => { - $('.action_submit_key').click(this.setHandlerPrevent('double', this.submitPublicKeyHandler)); - $('.action_replace_pubkey').click(this.setHandlerPrevent('double', this.replacePublicKeyHandler)); + $('.action_submit_key').on('click', this.setHandlerPrevent('double', this.submitPublicKeyHandler)); + $('.action_replace_pubkey').on('click', this.setHandlerPrevent('double', this.replacePublicKeyHandler)); }; // -- PRIVATE diff --git a/extension/chrome/settings/modules/my_key.htm b/extension/chrome/settings/modules/my_key.htm index 164d1e1d75b..044385f5844 100644 --- a/extension/chrome/settings/modules/my_key.htm +++ b/extension/chrome/settings/modules/my_key.htm @@ -40,7 +40,7 @@ class="action_download_revocation_cert display_none" data-test="action-revoke-certificate">revocation certificate
    + placeholder="Private Key Passphrase" maxlength="256"/>
    update private key
    diff --git a/extension/chrome/settings/modules/my_key_update.ts b/extension/chrome/settings/modules/my_key_update.ts index 1b728b19798..968db08aae0 100644 --- a/extension/chrome/settings/modules/my_key_update.ts +++ b/extension/chrome/settings/modules/my_key_update.ts @@ -65,7 +65,7 @@ View.run(class MyKeyUpdateView extends View { }; public setHandlers = () => { - $('.action_update_private_key').click(this.setHandlerPrevent('double', () => this.updatePrivateKeyHandler())); + $('.action_update_private_key').on('click', this.setHandlerPrevent('double', () => this.updatePrivateKeyHandler())); $('.input_passphrase').keydown(this.setEnterHandlerThatClicks('.action_update_private_key')); }; diff --git a/extension/chrome/settings/modules/security.htm b/extension/chrome/settings/modules/security.htm index 05b87f608c9..00f8caf2b92 100644 --- a/extension/chrome/settings/modules/security.htm +++ b/extension/chrome/settings/modules/security.htm @@ -41,7 +41,7 @@

    Private key pass phrase

    -
    +
    Password encrypted messages expire in diff --git a/extension/chrome/settings/modules/security.ts b/extension/chrome/settings/modules/security.ts index 9fc3f5bf1e0..373ea25aa45 100644 --- a/extension/chrome/settings/modules/security.ts +++ b/extension/chrome/settings/modules/security.ts @@ -50,8 +50,8 @@ View.run(class SecurityView extends View { }; public setHandlers = () => { - $('.action_change_passphrase').click(this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/change_passphrase.htm'))); - $('.action_test_passphrase').click(this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/test_passphrase.htm'))); + $('.action_change_passphrase').on('click', this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/change_passphrase.htm'))); + $('.action_test_passphrase').on('click', this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/test_passphrase.htm'))); $('#hide_message_password').change(this.setHandler((el) => this.hideMsgPasswordHandler(el))); $('.password_message_language').change(this.setHandler(() => this.onMsgLanguageUserChange())); }; @@ -59,11 +59,11 @@ View.run(class SecurityView extends View { private renderPassPhraseOptionsIfStoredPermanently = async () => { if (await this.isAnyPassPhraseStoredPermanently(this.prvs)) { $('.forget_passphrase').css('display', ''); - $('.action_forget_pp').click(this.setHandler(() => { + $('.action_forget_pp').on('click', this.setHandler(() => { $('.forget_passphrase').css('display', 'none'); $('.passphrase_entry_container').css('display', ''); })); - $('.confirm_passphrase_requirement_change').click(this.setHandler(async () => { + $('.confirm_passphrase_requirement_change').on('click', this.setHandler(async () => { const allPassPhrases = (await Promise.all(this.prvs.map(prv => PassphraseStore.get(this.acctEmail, prv.keyInfo)))) .filter(pp => !!pp); if (allPassPhrases.includes(String($('input#passphrase_entry').val()))) { @@ -77,7 +77,7 @@ View.run(class SecurityView extends View { $('input#passphrase_entry').val('').focus(); } })); - $('.cancel_passphrase_requirement_change').click(() => window.location.reload()); + $('.cancel_passphrase_requirement_change').on('click', () => window.location.reload()); $('#passphrase_entry').keydown(this.setEnterHandlerThatClicks('.confirm_passphrase_requirement_change')); } }; @@ -85,18 +85,21 @@ View.run(class SecurityView extends View { private loadAndRenderPwdEncryptedMsgSettings = async () => { Xss.sanitizeRender('.select_loader_container', Ui.spinner('green')); try { - const response = await this.acctServer.accountGetAndUpdateLocalStore(); - $('.select_loader_container').text(''); - $('.default_message_expire').val(Number(response.account.default_message_expire).toString()).prop('disabled', false).css('display', 'inline-block'); - $('.default_message_expire').change(this.setHandler(() => this.onDefaultExpireUserChange())); + if (!this.clientConfiguration.usesKeyManager()) { + $('.password_messages_expiry_container').show(); + const response = await this.acctServer.accountGetAndUpdateLocalStore(); + $('.select_loader_container').text(''); + $('.default_message_expire').val(Number(response.account.default_message_expire).toString()).prop('disabled', false).css('display', 'inline-block'); + $('.default_message_expire').change(this.setHandler(() => this.onDefaultExpireUserChange())); + } } catch (e) { if (ApiErr.isAuthErr(e)) { Settings.offerToLoginWithPopupShowModalOnErr(this.acctEmail, () => window.location.reload()); } else if (ApiErr.isNetErr(e)) { - Xss.sanitizeRender('.expiration_container', '(network error: retry)').find('a').click(() => window.location.reload()); // safe source + Xss.sanitizeRender('.expiration_container', '(network error: retry)').find('a').on('click', () => window.location.reload()); // safe source } else { Catch.reportErr(e); - Xss.sanitizeRender('.expiration_container', '(unknown error: retry)').find('a').click(() => window.location.reload()); // safe source + Xss.sanitizeRender('.expiration_container', '(unknown error: retry)').find('a').on('click', () => window.location.reload()); // safe source } } }; diff --git a/extension/chrome/settings/modules/test_passphrase.htm b/extension/chrome/settings/modules/test_passphrase.htm index 936c47e0a07..66b3d9db8e7 100644 --- a/extension/chrome/settings/modules/test_passphrase.htm +++ b/extension/chrome/settings/modules/test_passphrase.htm @@ -22,7 +22,7 @@
    We STRONGLY encourage you to check that you remember your passphrase correctly.
    - +
    diff --git a/extension/chrome/settings/modules/test_passphrase.ts b/extension/chrome/settings/modules/test_passphrase.ts index 12fadaf0413..a378a10efc9 100644 --- a/extension/chrome/settings/modules/test_passphrase.ts +++ b/extension/chrome/settings/modules/test_passphrase.ts @@ -43,9 +43,9 @@ View.run(class TestPassphrase extends View { }; public setHandlers = () => { - $('.action_verify').click(this.setHandler(() => this.verifyHandler())); + $('.action_verify').on('click', this.setHandler(() => this.verifyHandler())); $('#password').keydown(this.setEnterHandlerThatClicks('.action_verify')); - $('.action_change_passphrase').click(this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/change_passphrase.htm'))); + $('.action_change_passphrase').on('click', this.setHandler(() => Settings.redirectSubPage(this.acctEmail, this.parentTabId, '/chrome/settings/modules/change_passphrase.htm'))); }; private verifyHandler = async () => { @@ -54,7 +54,7 @@ View.run(class TestPassphrase extends View {
    ${Lang.setup.ppMatchAllSet}
    `); - $('.close').click(Ui.event.handle(() => BrowserMsg.send.closePage(this.parentTabId))); + $('.close').on('click', Ui.event.handle(() => BrowserMsg.send.closePage(this.parentTabId))); } else { await Ui.modal.warning('Pass phrase did not match. Please try again. If you forgot your pass phrase, please change it, so that you don\'t get locked out of your encrypted messages.'); } diff --git a/extension/chrome/settings/setup.htm b/extension/chrome/settings/setup.htm index 70661535cd8..2cbd64f15c0 100644 --- a/extension/chrome/settings/setup.htm +++ b/extension/chrome/settings/setup.htm @@ -110,10 +110,10 @@

    Set Up FlowCrypt

    data-swal-page="/chrome/texts/passphrase_help.htm">choosing secure pass phrases
    - +
    - +
    @@ -195,7 +195,7 @@

    Set Up FlowCrypt

    a random one.
    - +
    - +
    - +
    @@ -254,7 +254,7 @@

    Set Up FlowCrypt

    Found of your account key. Enter your pass phrase to continue.
    - +
    diff --git a/extension/chrome/settings/setup.ts b/extension/chrome/settings/setup.ts index 1ae859f6569..ea275ad8ee8 100644 --- a/extension/chrome/settings/setup.ts +++ b/extension/chrome/settings/setup.ts @@ -24,7 +24,7 @@ import { View } from '../../js/common/view.js'; import { Xss } from '../../js/common/platform/xss.js'; import { initPassphraseToggle } from '../../js/common/ui/passphrase-ui.js'; import { PubLookup } from '../../js/common/api/pub-lookup.js'; -import { Scopes, AcctStoreDict, AcctStore } from '../../js/common/platform/store/acct-store.js'; +import { AcctStoreDict, AcctStore } from '../../js/common/platform/store/acct-store.js'; import { KeyStore } from '../../js/common/platform/store/key-store.js'; import { KeyStoreUtil } from "../../js/common/core/crypto/key-store-util.js"; import { KeyManager } from '../../js/common/api/key-server/key-manager.js'; @@ -63,7 +63,6 @@ export class SetupView extends View { public readonly backupUi: BackupUi; public tabId!: string; - public scopes!: Scopes; public storage!: AcctStoreDict; public clientConfiguration!: ClientConfiguration; public pubLookup!: PubLookup; @@ -114,7 +113,6 @@ export class SetupView extends View { 'step_2_ekm_input_password', 'step_2_ekm_input_password2', 'recovery_password']); this.storage = await AcctStore.get(this.acctEmail, ['setup_done', 'email_provider', 'fesUrl']); - this.scopes = await AcctStore.getScopes(this.acctEmail); this.storage.email_provider = this.storage.email_provider || 'gmail'; this.clientConfiguration = await ClientConfiguration.newInstance(this.acctEmail); if (this.clientConfiguration.shouldHideArmorMeta() && typeof opgp !== 'undefined') { @@ -157,20 +155,24 @@ export class SetupView extends View { BrowserMsg.addListener('notification_show', async ({ notification }: Bm.NotificationShow) => { await Ui.modal.info(notification); }); BrowserMsg.listen(this.tabId); $('.action_send').attr('href', Google.webmailUrl(this.acctEmail)); - $('.action_show_help').click(this.setHandler(async () => await Settings.renderSubPage(this.acctEmail, this.tabId!, '/chrome/settings/modules/help.htm'))); - $('#button-go-back').off().click(this.setHandler(() => this.actionBackHandler())); - $('#step_2_ekm_choose_pass_phrase .action_proceed_private').click(this.setHandlerPrevent('double', () => this.setupWithEmailKeyManager.continueEkmSetupHandler())); - $('#step_2_recovery .action_recover_account').click(this.setHandlerPrevent('double', () => this.setupRecoverKey.actionRecoverAccountHandler())); - $('#step_4_more_to_recover .action_recover_remaining').click(this.setHandler(() => this.setupRecoverKey.actionRecoverRemainingKeysHandler())); - $('#lost_pass_phrase').click(this.setHandler(() => this.showLostPassPhraseModal())); - $('.action_account_settings').click(this.setHandler(() => { window.location.href = Url.create('index.htm', { acctEmail: this.acctEmail }); })); - $('.input_submit_key').click(this.setHandler(el => this.actionSubmitPublicKeyToggleHandler(el))); - $('#step_0_found_key .action_manual_create_key, #step_1_easy_or_manual .action_manual_create_key').click(this.setHandler(() => this.setupRender.displayBlock('step_2a_manual_create'))); - $('#step_0_found_key .action_manual_enter_key, #step_1_easy_or_manual .action_manual_enter_key').click(this.setHandler(() => this.setupRender.displayBlock('step_2b_manual_enter'))); - $('#step_2b_manual_enter .action_add_private_key').click(this.setHandler(el => this.setupImportKey.actionImportPrivateKeyHandle(el))); - $('#step_2a_manual_create .action_proceed_private').click(this.setHandlerPrevent('double', () => this.setupCreateKey.actionCreateKeyHandler())); - $('#step_2a_manual_create .action_show_advanced_create_settings').click(this.setHandler(el => this.setupCreateKey.actionShowAdvancedSettingsHandle(el))); - $('#step_4_close .action_close').click(this.setHandler(() => this.actionCloseHandler())); // only rendered if action=add_key which means parentTabId was used + $('.action_show_help').on('click', this.setHandler(async () => await Settings.renderSubPage(this.acctEmail, this.tabId!, '/chrome/settings/modules/help.htm'))); + $('#button-go-back').off().on('click', this.setHandler(() => this.actionBackHandler())); + $('#step_2_ekm_choose_pass_phrase .action_proceed_private').on('click', this.setHandlerPrevent('double', () => this.setupWithEmailKeyManager.continueEkmSetupHandler())); + $('#step_2_recovery .action_recover_account').on('click', this.setHandlerPrevent('double', () => this.setupRecoverKey.actionRecoverAccountHandler())); + $('#step_4_more_to_recover .action_recover_remaining').on('click', this.setHandler(() => this.setupRecoverKey.actionRecoverRemainingKeysHandler())); + $('#lost_pass_phrase').on('click', this.setHandler(() => this.showLostPassPhraseModal())); + $('.action_account_settings').on('click', this.setHandler(() => { window.location.href = Url.create('index.htm', { acctEmail: this.acctEmail }); })); + $('.input_submit_key').on('click', this.setHandler(el => this.actionSubmitPublicKeyToggleHandler(el))); + $('#step_0_found_key .action_manual_create_key, #step_1_easy_or_manual .action_manual_create_key').on('click', + this.setHandler(() => this.setupRender.displayBlock('step_2a_manual_create')) + ); + $('#step_0_found_key .action_manual_enter_key, #step_1_easy_or_manual .action_manual_enter_key').on('click', + this.setHandler(() => this.setupRender.displayBlock('step_2b_manual_enter')) + ); + $('#step_2b_manual_enter .action_add_private_key').on('click', this.setHandler(el => this.setupImportKey.actionImportPrivateKeyHandle(el))); + $('#step_2a_manual_create .action_proceed_private').on('click', this.setHandlerPrevent('double', () => this.setupCreateKey.actionCreateKeyHandler())); + $('#step_2a_manual_create .action_show_advanced_create_settings').on('click', this.setHandler(el => this.setupCreateKey.actionShowAdvancedSettingsHandle(el))); + $('#step_4_close .action_close').on('click', this.setHandler(() => this.actionCloseHandler())); // only rendered if action=add_key which means parentTabId was used $('#step_2a_manual_create .input_password').on('keydown', this.setEnterHandlerThatClicks('#step_2a_manual_create .action_proceed_private')); $('#step_2a_manual_create.input_password2').on('keydown', this.setEnterHandlerThatClicks('#step_2a_manual_create .action_proceed_private')); $('#step_2_ekm_choose_pass_phrase .input_password').on('keydown', this.setEnterHandlerThatClicks('#step_2_ekm_choose_pass_phrase .action_proceed_private')); @@ -201,8 +203,8 @@ export class SetupView extends View { Your previous encrypted emails will remain unreadable.
    `, true).catch(Catch.reportErr); - $('.action_skip_recovery').click(this.setHandler(() => this.setupRecoverKey.actionSkipRecoveryHandler())); - $('.reload_page').click(this.setHandler(() => window.location.reload())); + $('.action_skip_recovery').on('click', this.setHandler(() => this.setupRecoverKey.actionSkipRecoveryHandler())); + $('.reload_page').on('click', this.setHandler(() => window.location.reload())); }; public actionSubmitPublicKeyToggleHandler = (target: HTMLElement) => { diff --git a/extension/chrome/settings/setup/setup-recover-key.ts b/extension/chrome/settings/setup/setup-recover-key.ts index 0480cd550ae..f580e5b6b80 100644 --- a/extension/chrome/settings/setup/setup-recover-key.ts +++ b/extension/chrome/settings/setup/setup-recover-key.ts @@ -88,7 +88,7 @@ export class SetupRecoverKeyModule { if (this.view.action !== 'add_key') { Xss.sanitizeRender('#step_2_recovery .recovery_status', Lang.setup.nBackupsAlreadyRecoveredOrLeft(nImported, nFetched, txtKeysTeft)); Xss.sanitizeReplace('#step_2_recovery .line_skip_recovery', Ui.e('div', { class: 'line', html: Ui.e('a', { href: '#', class: 'skip_recover_remaining', html: 'Skip this step' }) })); - $('#step_2_recovery .skip_recover_remaining').click(this.view.setHandler(() => { window.location.href = Url.create('index.htm', { acctEmail: this.view.acctEmail }); })); + $('#step_2_recovery .skip_recover_remaining').on('click', this.view.setHandler(() => { window.location.href = Url.create('index.htm', { acctEmail: this.view.acctEmail }); })); } else { Xss.sanitizeRender('#step_2_recovery .recovery_status', `There ${txtKeysTeft} left to recover.

    Try different pass phrases to unlock all backups.`); $('#step_2_recovery .line_skip_recovery').css('display', 'none'); @@ -121,7 +121,7 @@ export class SetupRecoverKeyModule { const storedKeys = await KeyStore.get(this.view.acctEmail); this.view.importedKeysUniqueLongids = storedKeys.map(ki => ki.longid); await this.view.setupRender.renderSetupDone(); - $('#step_4_more_to_recover .action_recover_remaining').click(); + $('#step_4_more_to_recover .action_recover_remaining').trigger('click'); } else { window.location.href = Url.create('modules/add_key.htm', { acctEmail: this.view.acctEmail, parentTabId: this.view.parentTabId }); } diff --git a/extension/chrome/settings/setup/setup-render.ts b/extension/chrome/settings/setup/setup-render.ts index 3ce71440dc7..29224e6e4ea 100644 --- a/extension/chrome/settings/setup/setup-render.ts +++ b/extension/chrome/settings/setup/setup-render.ts @@ -111,7 +111,7 @@ export class SetupRenderModule { if (!this.view.clientConfiguration.canBackupKeys()) { // they already have a key recorded on attester, but no backups allowed on the domain. They should enter their prv manually this.displayBlock('step_2b_manual_enter'); - } else if (this.view.storage!.email_provider === 'gmail' && this.view.scopes!.modify) { + } else if (this.view.storage!.email_provider === 'gmail') { try { const backups = await this.view.gmail.fetchKeyBackups(); this.view.fetchedKeyBackups = backups.keyinfos.backups; @@ -148,7 +148,7 @@ export class SetupRenderModule { // eslint-disable-next-line max-len $('.addresses').append(`
    `); // xss-escaped } - $('.input_email_alias').click((event) => { + $('.input_email_alias').on('click', (event) => { const email = String($(event.target).data('email')); if ($(event.target).prop('checked')) { if (!this.view.submitKeyForAddrs.includes(email)) { diff --git a/extension/css/cryptup.css b/extension/css/cryptup.css index bdb8addcffe..ee8ab4c1e22 100644 --- a/extension/css/cryptup.css +++ b/extension/css/cryptup.css @@ -2260,6 +2260,8 @@ table#compose tr#password_or_pubkey_container td { } table#compose tr#password_or_pubkey_container td input#input_password::placeholder { color: #d14836; } +table#compose tr#password_or_pubkey_container td input#input_password:focus::placeholder { color: transparent; } + table#compose tr#password_or_pubkey_container td input { padding-left: 3px; padding-right: 3px; @@ -2276,17 +2278,6 @@ table#compose tr#password_or_pubkey_container td input#input_password { text-align: center; } -table#compose tr#password_or_pubkey_container td .label_password { - position: absolute; - margin-left: 10px; - margin-top: 22px; - font-size: 10px; - text-transform: uppercase; - color: #777; - display: none; - animation: fadeInDown 0.5s; -} - table#compose tr#password_or_pubkey_container td .grey { padding: 1px 5px; background: #989898; diff --git a/extension/css/webmail.css b/extension/css/webmail.css index ffbf6ed9afd..6170eecc655 100644 --- a/extension/css/webmail.css +++ b/extension/css/webmail.css @@ -104,6 +104,8 @@ /* notifications */ div.webmail_notifications { + display: flex; + flex-direction: column; position: absolute; top: 0; left: 0; @@ -124,10 +126,8 @@ div.webmail_notifications div.webmail_notification { line-height: 16px; padding: 6px 10px; z-index: 999; - margin-top: 50px; - display: inline-block; - margin-right: 50px; - margin-left: 50px; + margin: 50px auto 0; + max-width: 90%; } div.webmail_notifications div.webmail_notification .webmail_notification_buttons { @@ -143,9 +143,18 @@ div.webmail_notifications div.webmail_notification a { padding-left: 6px; padding-right: 2px; } -div.webmail_notifications div.webmail_notification a:hover { text-decoration: none; } -body.cryptup_inbox div.webmail_notifications { position: fixed; } -body.cryptup_outlook div.webmail_notifications div.webmail_notification { margin-top: 10px; } + +div.webmail_notifications div.webmail_notification a:hover { + text-decoration: none; +} + +body.cryptup_inbox div.webmail_notifications { + position: fixed; +} + +body.cryptup_outlook div.webmail_notifications div.webmail_notification { + margin-top: 10px; +} .error_notification { background: #c53929; @@ -177,7 +186,12 @@ body.cryptup_inbox div.new_secure_compose_window_button > img { padding: 6px; padding-top: 10px; } -body.cryptup_inbox .pgp_message_container .nk .tq:first-child { display: none !important; } /* reply, reply all, forward in menu next to encrypted messages */ + +body.cryptup_inbox .pgp_message_container .nk .tq:first-child { + display: none !important; +} + +/* reply, reply all, forward in menu next to encrypted messages */ /* buttons body.cryptup_gmail */ body.cryptup_gmail .inserted div.reply_message_button { @@ -190,13 +204,18 @@ body.cryptup_gmail .inserted div.reply_message_button { height: auto; } -@-moz-document url-prefix('') { /* Firefox specific */ +@-moz-document url-prefix('') { + /* Firefox specific */ body.cryptup_gmail .inserted div.reply_message_button { padding-bottom: 4px; } } -body.cryptup_gmail.firefox .inserted div.reply_message_button { padding-top: 16px; } /* else buttons misaligned vertically on FF */ +body.cryptup_gmail.firefox .inserted div.reply_message_button { + padding-top: 16px; +} + +/* else buttons misaligned vertically on FF */ body.cryptup_gmail .inserted div.reply_message_button:hover { opacity: 0.9; cursor: pointer; @@ -210,9 +229,18 @@ body.cryptup_gmail div.reply_message_button > img { top: 3px; margin: 0 auto; } -body.cryptup_gmail_standard div.new_secure_compose_window_button { background: #31a217; } -body.cryptup_gmail_standard .z0 .T-I { min-width: 105px; } -body.cryptup_gmail_new div.new_secure_compose_window_button:hover { background-color: #fafafb; } + +body.cryptup_gmail_standard div.new_secure_compose_window_button { + background: #31a217; +} + +body.cryptup_gmail_standard .z0 .T-I { + min-width: 105px; +} + +body.cryptup_gmail_new div.new_secure_compose_window_button:hover { + background-color: #fafafb; +} body.cryptup_gmail div.ade.appended span.cryptup_convo_button > img { border: 2px solid #31a217; @@ -235,8 +263,13 @@ body.cryptup_gmail div.ade.appended span.cryptup_convo_button > span { } body.cryptup_gmail div.ade.appended span.cryptup_convo_button:hover > img, -div.ade.appended span.cryptup_convo_button:hover > span { opacity: 1; } -body.cryptup_gmail div.ade.appended span.cryptup_convo_button:not(:first-child) { margin-left: 5px; } +div.ade.appended span.cryptup_convo_button:hover > span { + opacity: 1; +} + +body.cryptup_gmail div.ade.appended span.cryptup_convo_button:not(:first-child) { + margin-left: 5px; +} body.cryptup_gmail.cryptup_gmail_new div.ade.appended span.cryptup_convo_button { margin-left: 18px; @@ -289,7 +322,12 @@ body.cryptup_outlook .cryptup_compose_button_container .new_secure_compose_windo height: 19px; cursor: pointer; } -body.cryptup_outlook ._fce_i { padding: 0 6px; } /* slightly less horizontal padding on other outlook buttons in the same menu so that it doesn't misbehave */ + +body.cryptup_outlook ._fce_i { + padding: 0 6px; +} + +/* slightly less horizontal padding on other outlook buttons in the same menu so that it doesn't misbehave */ /* elements */ .secure_compose_window { @@ -351,18 +389,25 @@ body.cryptup_outlook ._fce_i { padding: 0 6px; } /* slightly less horizontal pad margin: 0; overflow: hidden; } -.secure_compose_window.full_window iframe { height: 100%; } + +.secure_compose_window.full_window iframe { + height: 100%; +} @media screen and (max-height: 600px) { .secure_compose_window, - .secure_compose_window iframe { height: 450px; } + .secure_compose_window iframe { + height: 450px; + } } div.evaluated iframe.embedded { width: 500px; height: 160px; border: 1px solid #cacaca; -} /* verification results */ +} + +/* verification results */ iframe.pgp_block { width: 100%; border: none; @@ -473,7 +518,9 @@ body.cryptup_gmail div.recipients_use_encryption a { padding: 0; } -.swal2-popup.ui-modal-iframe .swal2-close { color: #999; } +.swal2-popup.ui-modal-iframe .swal2-close { + color: #999; +} .swal2-popup.ui-modal-iframe iframe { display: flex; @@ -517,4 +564,6 @@ body.cryptup_gmail div.recipients_use_encryption a { margin: 0 0.6em 0.3em; } -.swal2-popup.swal2-toast.ui-toast .swal2-timer-progress-bar { background: rgba(255, 255, 255, 0.5); } +.swal2-popup.swal2-toast.ui-toast .swal2-timer-progress-bar { + background: rgba(255, 255, 255, 0.5); +} diff --git a/extension/js/background_page/background_page.ts b/extension/js/background_page/background_page.ts index 5eba0fa4225..b0743ae9185 100644 --- a/extension/js/background_page/background_page.ts +++ b/extension/js/background_page/background_page.ts @@ -7,7 +7,7 @@ import { Bm, BrowserMsg } from '../common/browser/browser-msg.js'; import { emailKeyIndex } from '../common/core/common.js'; import { VERSION } from '../common/core/const.js'; import { ExpirationCache } from '../common/core/expiration-cache.js'; -import { processAndStoreKeysFromEkmLocally } from '../common/helpers.js'; +import { processAndStoreKeysFromEkmLocally, getLocalKeyExpiration } from '../common/helpers.js'; import { Catch } from '../common/platform/catch.js'; import { AcctStore } from '../common/platform/store/acct-store.js'; import { ContactStore } from '../common/platform/store/contact-store.js'; @@ -62,6 +62,7 @@ console.info('background_process.js starting'); BrowserMsg.bgAddListener('storeAcctGet', (r: Bm.StoreAcctGet) => AcctStore.get(r.acctEmail, r.keys)); BrowserMsg.bgAddListener('storeAcctSet', (r: Bm.StoreAcctSet) => AcctStore.set(r.acctEmail, r.values)); BrowserMsg.bgAddListener('processAndStoreKeysFromEkmLocally', processAndStoreKeysFromEkmLocally); + BrowserMsg.bgAddListener('getLocalKeyExpiration', getLocalKeyExpiration); // todo - when https://github.com/FlowCrypt/flowcrypt-browser/issues/2560 // is fixed, this can be moved to the gmail content script, and some may be removed diff --git a/extension/js/common/api/account-servers/enterprise-server.ts b/extension/js/common/api/account-servers/enterprise-server.ts index 576a30c16ae..09db7e91b58 100644 --- a/extension/js/common/api/account-servers/enterprise-server.ts +++ b/extension/js/common/api/account-servers/enterprise-server.ts @@ -15,7 +15,7 @@ import { FLAVOR, InMemoryStoreKeys } from '../../core/const.js'; import { Attachment } from '../../core/attachment.js'; import { ParsedRecipients } from '../email-provider/email-provider-api.js'; import { Buf } from '../../core/buf.js'; -import { ClientConfigurationJson } from '../../client-configuration.js'; +import { ClientConfigurationError, ClientConfigurationJson } from '../../client-configuration.js'; import { InMemoryStore } from '../../platform/store/in-memory-store.js'; // todo - decide which tags to use @@ -90,6 +90,9 @@ export class EnterpriseServer extends Api { public fetchAndSaveClientConfiguration = async (): Promise => { const r = await this.request('GET', `/api/${this.apiVersion}/client-configuration?domain=${this.domain}`); + if (r.clientConfiguration && !r.clientConfiguration.flags) { + throw new ClientConfigurationError('missing_flags'); + } await AcctStore.set(this.acctEmail, { rules: r.clientConfiguration }); return r.clientConfiguration; }; diff --git a/extension/js/common/api/account-servers/flowcrypt-com-api.ts b/extension/js/common/api/account-servers/flowcrypt-com-api.ts index fca54d4c5a8..9f90a98acf1 100644 --- a/extension/js/common/api/account-servers/flowcrypt-com-api.ts +++ b/extension/js/common/api/account-servers/flowcrypt-com-api.ts @@ -8,7 +8,7 @@ import { Api, ProgressCb, ProgressCbs, ReqFmt } from '../shared/api.js'; import { Dict } from '../../core/common.js'; import { Attachment } from '../../core/attachment.js'; -import { ClientConfigurationJson } from '../../client-configuration.js'; +import { ClientConfigurationError, ClientConfigurationJson } from '../../client-configuration.js'; import { AcctStore } from '../../platform/store/acct-store.js'; import { FlowCryptWebsite } from '../flowcrypt-website.js'; import { GoogleAuth } from '../email-provider/gmail/google-auth.js'; @@ -45,6 +45,9 @@ export class FlowCryptComApi extends Api { if (!email) { throw new Error('Id token is invalid'); } + if (r.domain_org_rules && !r.domain_org_rules.flags) { + throw new ClientConfigurationError('missing_flags'); + } await AcctStore.set(email, { rules: r.domain_org_rules }); return r; }; @@ -55,9 +58,8 @@ export class FlowCryptComApi extends Api { 'message/upload', { content }, 'FORM', - undefined, + FlowCryptComApi.getAuthorizationHeader(idToken), { - ...FlowCryptComApi.getAuthorizationHeader(idToken), upload: progressCb } ); diff --git a/extension/js/common/api/email-provider/gmail/gmail.ts b/extension/js/common/api/email-provider/gmail/gmail.ts index 5371606a322..452e8d5b841 100644 --- a/extension/js/common/api/email-provider/gmail/gmail.ts +++ b/extension/js/common/api/email-provider/gmail/gmail.ts @@ -291,7 +291,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface { return { armored: fromHtmlBody, subject, isPwdMsg }; } for (const attachment of attachments) { - if (attachment.treatAs() === 'encryptedMsg') { + if (attachment.treatAs(!!textBody) === 'encryptedMsg') { await this.fetchAttachments([attachment], progressCb); const armoredMsg = PgpArmor.clip(attachment.getData().toUtfStr()); if (!armoredMsg) { diff --git a/extension/js/common/api/email-provider/gmail/google-auth.ts b/extension/js/common/api/email-provider/gmail/google-auth.ts index 8b5d983b2f6..d55d0425e50 100644 --- a/extension/js/common/api/email-provider/gmail/google-auth.ts +++ b/extension/js/common/api/email-provider/gmail/google-auth.ts @@ -5,8 +5,8 @@ // tslint:disable:no-direct-ajax // tslint:disable:oneliner-object-literal -import { Str, Url, Value } from '../../../core/common.js'; -import { FLAVOR, GOOGLE_OAUTH_SCREEN_HOST, OAUTH_GOOGLE_API_HOST } from '../../../core/const.js'; +import { Str, Url } from '../../../core/common.js'; +import { FLAVOR, GMAIL_GOOGLE_API_HOST, GOOGLE_OAUTH_SCREEN_HOST, OAUTH_GOOGLE_API_HOST } from '../../../core/const.js'; import { ApiErr } from '../../shared/api-error.js'; import { Api } from './../../shared/api.js'; @@ -28,6 +28,7 @@ type AuthResultSuccess = { result: 'Success', acctEmail: string, id_token: strin type AuthResultError = { result: GoogleAuthWindowResult$result, acctEmail?: string, error?: string, id_token: undefined }; type AuthReq = { acctEmail?: string, scopes: string[], messageId?: string, expectedState: string }; +type GoogleTokenInfo = { email: string, scope: string, expires_in: number, token_type: string }; export type AuthRes = AuthResultSuccess | AuthResultError; export class GoogleAuth { @@ -54,11 +55,9 @@ export class GoogleAuth { } }; - public static defaultScopes = (group: 'default' | 'contacts' | 'openid' = 'default') => { + public static defaultScopes = (group: 'default' | 'contacts' = 'default') => { const { readContacts, readOtherContacts, compose, modify, openid, email, profile } = GoogleAuth.OAUTH.scopes; - if (group === 'openid') { - return [openid, email, profile]; - } else if (group === 'default') { + if (group === 'default') { if (FLAVOR === 'consumer') { return [openid, email, profile, compose, modify]; // consumer may freak out that extension asks for their contacts early on } else if (FLAVOR === 'enterprise') { @@ -73,12 +72,22 @@ export class GoogleAuth { } }; + public static getTokenInfo = async (accessToken: string): Promise => { + return await Api.ajax( + { + url: `${GMAIL_GOOGLE_API_HOST}/oauth2/v1/tokeninfo?access_token=${accessToken}`, + timeout: 10000 + }, + Catch.stackTrace() + ) as unknown as GoogleTokenInfo; + }; + public static googleApiAuthHeader = async (acctEmail: string, forceRefresh = false): Promise => { if (!acctEmail) { throw new Error('missing account_email in api_gmail_call'); } - const storage = await AcctStore.get(acctEmail, ['google_token_scopes', 'google_token_refresh']); - if (!storage.google_token_refresh) { + const { google_token_refresh } = await AcctStore.get(acctEmail, ['google_token_refresh']); + if (!google_token_refresh) { throw new GoogleAuthErr(`Account ${acctEmail} not connected to FlowCrypt Browser Extension`); } if (!forceRefresh) { @@ -88,9 +97,9 @@ export class GoogleAuth { } } // refresh token - const refreshTokenRes = await GoogleAuth.googleAuthRefreshToken(storage.google_token_refresh); + const refreshTokenRes = await GoogleAuth.googleAuthRefreshToken(google_token_refresh); if (refreshTokenRes.access_token) { - await GoogleAuth.googleAuthSaveTokens(acctEmail, refreshTokenRes, storage.google_token_scopes || []); + await GoogleAuth.googleAuthSaveTokens(acctEmail, refreshTokenRes); const googleAccessToken = await InMemoryStore.get(acctEmail, InMemoryStoreKeys.GOOGLE_TOKEN_ACCESS); if (googleAccessToken) { return `Bearer ${googleAccessToken}`; @@ -119,7 +128,7 @@ export class GoogleAuth { save = true; } if (save || !scopes) { // if tokens will be saved (meaning also scopes should be pulled from storage) or if no scopes supplied - scopes = await GoogleAuth.apiGoogleAuthPopupPrepareAuthReqScopes(acctEmail, scopes || GoogleAuth.defaultScopes()); + scopes = await GoogleAuth.apiGoogleAuthPopupPrepareAuthReqScopes(scopes || GoogleAuth.defaultScopes()); } const authRequest = GoogleAuth.newAuthRequest(acctEmail, scopes); const authUrl = GoogleAuth.apiGoogleAuthCodeUrl(authRequest); @@ -172,10 +181,6 @@ export class GoogleAuth { return false; }; - public static newOpenidAuthPopup = async ({ acctEmail }: { acctEmail?: string }): Promise => { - return await GoogleAuth.newAuthPopup({ acctEmail, scopes: GoogleAuth.defaultScopes('openid'), save: false }); - }; - // todo - would be better to use a TS type guard instead of the type cast when checking OpenId // check for things we actually use: photo/name/locale public static parseIdToken = (idToken: string): GmailRes.OpenId => { @@ -219,7 +224,7 @@ export class GoogleAuth { if (receivedState !== expectedState) { return { acctEmail, result: 'Error', error: `Wrong oauth CSRF token. Please try again.`, id_token: undefined }; } - const { id_token } = save ? await GoogleAuth.retrieveAndSaveAuthToken(code, allowedScopes?.replace(/\+/g, ' ')?.split(' ') ?? []) : await GoogleAuth.googleAuthGetTokens(code); + const { id_token } = save ? await GoogleAuth.retrieveAndSaveAuthToken(code) : await GoogleAuth.googleAuthGetTokens(code); const { email } = GoogleAuth.parseIdToken(id_token); if (!email) { throw new Error('Missing email address in id_token'); @@ -258,12 +263,11 @@ export class GoogleAuth { }); }; - private static googleAuthSaveTokens = async (acctEmail: string, tokensObj: GoogleAuthTokensResponse, scopes: string[]) => { + private static googleAuthSaveTokens = async (acctEmail: string, tokensObj: GoogleAuthTokensResponse) => { const parsedOpenId = GoogleAuth.parseIdToken(tokensObj.id_token); const { full_name, picture } = await AcctStore.get(acctEmail, ['full_name', 'picture']); const googleTokenExpires = new Date().getTime() + (tokensObj.expires_in as number - 120) * 1000; // let our copy expire 2 minutes beforehand const toSave: AcctStoreDict = { - google_token_scopes: scopes, full_name: full_name || parsedOpenId.name, picture: picture || parsedOpenId.picture, }; @@ -307,22 +311,17 @@ export class GoogleAuth { }, Catch.stackTrace()) as unknown as GoogleAuthTokensResponse; }; - private static retrieveAndSaveAuthToken = async (authCode: string, scopes: string[]): Promise<{ id_token: string }> => { + private static retrieveAndSaveAuthToken = async (authCode: string): Promise<{ id_token: string }> => { const tokensObj = await GoogleAuth.googleAuthGetTokens(authCode); const claims = GoogleAuth.parseIdToken(tokensObj.id_token); if (!claims.email) { throw new Error('Missing email address in id_token'); } - await GoogleAuth.googleAuthSaveTokens(claims.email, tokensObj, scopes); + await GoogleAuth.googleAuthSaveTokens(claims.email, tokensObj); return { id_token: tokensObj.id_token }; }; - private static apiGoogleAuthPopupPrepareAuthReqScopes = async (acctEmail: string | undefined, addScopes: string[]): Promise => { - if (acctEmail) { - const { google_token_scopes } = await AcctStore.get(acctEmail, ['google_token_scopes']); - addScopes.push(...(google_token_scopes || [])); - } - addScopes = Value.arr.unique(addScopes); + private static apiGoogleAuthPopupPrepareAuthReqScopes = async (addScopes: string[]): Promise => { if (!addScopes.includes(GoogleAuth.OAUTH.scopes.email)) { addScopes.push(GoogleAuth.OAUTH.scopes.email); } diff --git a/extension/js/common/assert.ts b/extension/js/common/assert.ts index 45a75fd0222..1e582d69cd3 100644 --- a/extension/js/common/assert.ts +++ b/extension/js/common/assert.ts @@ -85,7 +85,7 @@ export class Assert { const msg = `Cannot render page (expected ${Xss.escape(name)} to be of type ${Xss.escape(expectedType)} but got ${Xss.escape(actualType)})`; const renderMsg = `${msg}

    `; Xss.sanitizeRender('body', renderMsg).addClass('bad').css({ padding: '20px', 'font-size': '16px' }); - $('.action_report_issue').click(Ui.event.handle(async () => { + $('.action_report_issue').on('click', Ui.event.handle(async () => { Catch.report(msg, { currentUrl: window.location.href, params: values }); $('body').text(`Thank you. ${Lang.general.contactIfNeedAssistance()}`); })); diff --git a/extension/js/common/browser/browser-msg.ts b/extension/js/common/browser/browser-msg.ts index 71a4b72281a..c3816d84c52 100644 --- a/extension/js/common/browser/browser-msg.ts +++ b/extension/js/common/browser/browser-msg.ts @@ -8,6 +8,7 @@ import { Buf } from '../core/buf.js'; import { Dict, Str, UrlParams } from '../core/common.js'; import { ArmoredKeyIdentityWithEmails, KeyUtil } from '../core/crypto/key.js'; import { DecryptResult, DiagnoseMsgPubkeysResult, MsgUtil, PgpMsgMethod, PgpMsgTypeResult, VerifyRes } from '../core/crypto/pgp/msg-util.js'; +import { NotificationGroupType } from '../notifications.js'; import { Catch } from '../platform/catch.js'; import { AccountIndex, AcctStoreDict } from '../platform/store/acct-store.js'; import { GlobalIndex, GlobalStoreDict } from '../platform/store/global-store.js'; @@ -33,7 +34,7 @@ export namespace Bm { export type PassphraseDialog = { type: PassphraseDialogType, longids: string[], initiatorFrameId?: string }; export type ScrollToReplyBox = { replyMsgId: string }; export type ScrollToCursorInReplyBox = { replyMsgId: string, cursorOffsetTop: number }; - export type NotificationShow = { notification: string, callbacks?: Dict<() => void> }; + export type NotificationShow = { notification: string, group: NotificationGroupType, callbacks?: Dict<() => void> }; export type NotificationShowAuthPopupNeeded = { acctEmail: string }; export type RenderPublicKeys = { afterFrameId: string, publicKeys: string[], traverseUp?: number }; export type SubscribeDialog = { isAuthErr?: boolean }; @@ -66,6 +67,7 @@ export namespace Bm { export type ReRenderRecipient = { email: string }; export type SaveFetchedPubkeys = { email: string, pubkeys: string[] }; export type ProcessAndStoreKeysFromEkmLocally = { acctEmail: string, decryptedPrivateKeys: string[] }; + export type GetLocalKeyExpiration = { acctEmail: string }; export namespace Res { export type GetActiveTabInfo = { provider: 'gmail' | undefined, acctEmail: string | undefined, sameWorld: boolean | undefined }; @@ -84,6 +86,7 @@ export namespace Bm { export type AjaxGmailAttachmentGetChunk = { chunk: Buf }; export type _tab_ = { tabId: string | null | undefined }; export type SaveFetchedPubkeys = boolean; + export type GetLocalKeyExpiration = number | undefined; export type ProcessAndStoreKeysFromEkmLocally = { needPassphrase?: boolean, updateCount?: number, noKeysSetup?: boolean }; // eslint-disable-next-line @typescript-eslint/no-explicit-any export type Db = any; // not included in Any below @@ -94,7 +97,7 @@ export namespace Bm { | PgpMsgDecrypt | PgpMsgDiagnoseMsgPubkeys | PgpMsgVerify | PgpMsgType | InMemoryStoreGet | InMemoryStoreSet | StoreAcctGet | StoreAcctSet | StoreGlobalGet | StoreGlobalSet | AjaxGmailAttachmentGetChunk | SaveFetchedPubkeys | ProcessAndStoreKeysFromEkmLocally - | PgpKeyBinaryToArmored; + | PgpKeyBinaryToArmored | GetLocalKeyExpiration; } export type AnyRequest = PassphraseEntry | OpenPage | OpenGoogleAuthDialog | Redirect | Reload | @@ -103,7 +106,7 @@ export namespace Bm { NotificationShow | PassphraseDialog | PassphraseDialog | Settings | SetCss | AddOrRemoveClass | ReconnectAcctAuthPopup | Db | InMemoryStoreSet | InMemoryStoreGet | StoreGlobalGet | StoreGlobalSet | StoreAcctGet | StoreAcctSet | PgpMsgDecrypt | PgpMsgDiagnoseMsgPubkeys | PgpMsgVerifyDetached | PgpMsgType | Ajax | - ShowAttachmentPreview | ReRenderRecipient | SaveFetchedPubkeys | ProcessAndStoreKeysFromEkmLocally | + ShowAttachmentPreview | ReRenderRecipient | SaveFetchedPubkeys | ProcessAndStoreKeysFromEkmLocally | GetLocalKeyExpiration | PgpKeyBinaryToArmored | AuthWindowResult; // export type RawResponselessHandler = (req: AnyRequest) => Promise; @@ -154,6 +157,7 @@ export class BrowserMsg { saveFetchedPubkeys: (bm: Bm.SaveFetchedPubkeys) => BrowserMsg.sendAwait(undefined, 'saveFetchedPubkeys', bm, true) as Promise, processAndStoreKeysFromEkmLocally: (bm: Bm.ProcessAndStoreKeysFromEkmLocally) => BrowserMsg.sendAwait(undefined, 'processAndStoreKeysFromEkmLocally', bm, true) as Promise, + getLocalKeyExpiration: (bm: Bm.GetLocalKeyExpiration) => BrowserMsg.sendAwait(undefined, 'getLocalKeyExpiration', bm, true) as Promise, }, }, passphraseEntry: (dest: Bm.Dest, bm: Bm.PassphraseEntry) => BrowserMsg.sendCatch(dest, 'passphrase_entry', bm), diff --git a/extension/js/common/browser/ui.ts b/extension/js/common/browser/ui.ts index 5fd9e73357c..7ebce87fdb6 100644 --- a/extension/js/common/browser/ui.ts +++ b/extension/js/common/browser/ui.ts @@ -303,7 +303,7 @@ export class Ui { public static testCompatibilityLink = 'Test your OpenPGP key compatibility'; public static activateModalPageLinkTags = () => { - $('[data-swal-page]').click(Ui.event.handle(async (target) => { + $('[data-swal-page]').on('click', Ui.event.handle(async (target) => { const jsAllowedSwalPage = $(target).data('swal-page-allow-js') as boolean; // use this flag is the swal-page contains javascript const htmlUrl = $(target).data('swal-page') as string; if (jsAllowedSwalPage) { diff --git a/extension/js/common/client-configuration.ts b/extension/js/common/client-configuration.ts index cf991e4197f..5e02d465585 100644 --- a/extension/js/common/client-configuration.ts +++ b/extension/js/common/client-configuration.ts @@ -9,7 +9,7 @@ import { UnreportableError } from './platform/catch.js'; type ClientConfiguration$flag = 'NO_PRV_CREATE' | 'NO_PRV_BACKUP' | 'PRV_AUTOIMPORT_OR_AUTOGEN' | 'PASS_PHRASE_QUIET_AUTOGEN' | 'ENFORCE_ATTESTER_SUBMIT' | 'NO_ATTESTER_SUBMIT' | 'SETUP_ENSURE_IMPORTED_PRV_MATCH_LDAP_PUB' | - 'DEFAULT_REMEMBER_PASS_PHRASE' | 'HIDE_ARMOR_META' | 'FORBID_STORING_PASS_PHRASE'; + 'DEFAULT_REMEMBER_PASS_PHRASE' | 'HIDE_ARMOR_META' | 'FORBID_STORING_PASS_PHRASE' | 'DISABLE_FLOWCRYPT_HOSTED_PASSWORD_MESSAGES'; export type ClientConfigurationJson = { flags?: ClientConfiguration$flag[], @@ -22,6 +22,17 @@ export type ClientConfigurationJson = { in_memory_pass_phrase_session_length?: number; }; +type ClientConfigurationErrorType = 'missing_flags'; +export class ClientConfigurationError extends UnreportableError { + constructor(errorType: ClientConfigurationErrorType) { + let errorMsg = ''; + if (errorType === 'missing_flags') { + errorMsg = 'Missing client configuration flags.'; + } + super(errorMsg); + } +} + /** * Organisational rules, set domain-wide, and delivered from FlowCrypt Backend * These either enforce, alter or forbid various behavior to fit customer needs @@ -35,7 +46,7 @@ export class ClientConfiguration { } const storage = await AcctStore.get(email, ['rules']); if (storage.rules && !storage.rules.flags) { - throw new UnreportableError('Missing client configuration flags.'); + throw new ClientConfigurationError('missing_flags'); } return new ClientConfiguration(storage.rules ?? {}, Str.getDomainFromEmailAddress(acctEmail)); }; @@ -207,4 +218,12 @@ export class ClientConfiguration { return (this.clientConfigurationJson.flags || []).includes('HIDE_ARMOR_META'); }; + /** + * with this option and recipients are missing a public key, and the user is using flowcrypt.com/api (not FES) + * it will not give the user option to enter a message password, as if that functionality didn't exist. + */ + public shouldDisablePasswordMessages = (): boolean => { + return (this.clientConfigurationJson.flags || []).includes('DISABLE_FLOWCRYPT_HOSTED_PASSWORD_MESSAGES'); + }; + } diff --git a/extension/js/common/core/attachment.ts b/extension/js/common/core/attachment.ts index 3c64dedaee1..f049718b7c9 100644 --- a/extension/js/common/core/attachment.ts +++ b/extension/js/common/core/attachment.ts @@ -18,7 +18,7 @@ export class Attachment { // eslint-disable-next-line max-len public static readonly webmailNamePattern = /^(((cryptup|flowcrypt)-backup-[a-z0-9]+\.(key|asc))|(.+\.pgp)|(.+\.gpg)|(.+\.asc)|(noname)|(message)|(PGPMIME version identification)|(ATT[0-9]{5})|())$/m; - public static readonly encryptedMsgNames = ['message', 'msg.asc', 'message.asc', 'encrypted.asc', 'encrypted.eml.pgp', 'Message.pgp', 'openpgp-encrypted-message.asc']; + public static readonly encryptedMsgNames = ['msg.asc', 'message.asc', 'encrypted.asc', 'encrypted.eml.pgp', 'Message.pgp', 'openpgp-encrypted-message.asc']; public length: number = NaN; public type: string; @@ -105,7 +105,7 @@ export class Attachment { throw new Error('Attachment has no data set'); }; - public treatAs = (): Attachment$treatAs => { + public treatAs = (isBodyEmpty = false): Attachment$treatAs => { if (this.treatAsValue) { // pre-set return this.treatAsValue; } else if (['PGPexch.htm.pgp', 'PGPMIME version identification', 'Version.txt', 'PGPMIME Versions Identification'].includes(this.name)) { @@ -118,6 +118,9 @@ export class Attachment { return 'hidden'; // mail.ch does this - although it looks like encrypted msg, it will just contain PGP version eg "Version: 1" } else if (Attachment.encryptedMsgNames.includes(this.name)) { return 'encryptedMsg'; + } else if (this.name === 'message' && isBodyEmpty) { + // treat message as encryptedMsg when empty body for the 'message' attachment + return 'encryptedMsg'; } else if (this.name.match(/(\.pgp$)|(\.gpg$)|(\.[a-zA-Z0-9]{3,4}\.asc$)/g)) { // ends with one of .gpg, .pgp, .???.asc, .????.asc return 'encryptedFile'; } else if (this.name.match(/(cryptup|flowcrypt)-backup-[a-z0-9]+\.(key|asc)$/g)) { diff --git a/extension/js/common/core/common.ts b/extension/js/common/core/common.ts index 83b4ed4d536..49776e645cb 100644 --- a/extension/js/common/core/common.ts +++ b/extension/js/common/core/common.ts @@ -362,4 +362,8 @@ export const asyncSome = async(arr: Array, predicate: (e: T) => Promise(...data: T): T => { + return data; }; \ No newline at end of file diff --git a/extension/js/common/core/mime.ts b/extension/js/common/core/mime.ts index 4292c2305a3..92e5632b05c 100644 --- a/extension/js/common/core/mime.ts +++ b/extension/js/common/core/mime.ts @@ -69,7 +69,8 @@ export class Mime { blocks.push(MsgBlock.fromContent('plainHtml', decoded.html)); } for (const file of decoded.attachments) { - const treatAs = file.treatAs(); + const isBodyEmpty = decoded.text === '' || decoded.text === '\n'; + const treatAs = file.treatAs(isBodyEmpty); if (treatAs === 'encryptedMsg') { const armored = PgpArmor.clip(file.getData().toUtfStr()); if (armored) { diff --git a/extension/js/common/core/msg-block-parser.ts b/extension/js/common/core/msg-block-parser.ts index 3bfb81332f9..7de11356e6a 100644 --- a/extension/js/common/core/msg-block-parser.ts +++ b/extension/js/common/core/msg-block-parser.ts @@ -90,6 +90,10 @@ export class MsgBlockParser { // thus we use RegEx so that it works on both browser and node if (decryptedContent.includes('class="cryptup_file"')) { decryptedContent = decryptedContent.replace(/[^<]+<\/a>\n?/gm, (_, url, fcData) => { + const fcAttachmentHost = new URL(String(url)).host; + if (fcAttachmentHost !== 'flowcrypt.s3.amazonaws.com') { + return '[skipped attachment due to invalid url]'; + } const a = Str.htmlAttrDecode(String(fcData)); if (MsgBlockParser.isFcAttachmentLinkData(a)) { blocks.push(MsgBlock.fromAttachment('encryptedAttachmentLink', '', { type: a.type, name: a.name, length: a.size, url: String(url) })); diff --git a/extension/js/common/helpers.ts b/extension/js/common/helpers.ts index fcc6421de08..bcca1d549c0 100644 --- a/extension/js/common/helpers.ts +++ b/extension/js/common/helpers.ts @@ -164,3 +164,9 @@ export const processAndStoreKeysFromEkmLocally = async ( } return { updateCount: encryptedKeys?.keys.length ?? 0 + (existingKeys.length - keysToRetain.length), noKeysSetup: !(encryptedKeys?.keys.length || keysToRetain.length) }; }; + +export const getLocalKeyExpiration = async ({ acctEmail }: Bm.GetLocalKeyExpiration): Promise => { + const kis = await KeyStore.get(acctEmail); + const expirations = await Promise.all(kis.map(async (ki) => (await KeyUtil.parse(ki.public))?.expiration ?? Number.MAX_SAFE_INTEGER)); + return Math.max(...expirations); +}; \ No newline at end of file diff --git a/extension/js/common/inject.ts b/extension/js/common/inject.ts index 668d3d978f1..50c9e0f3a93 100644 --- a/extension/js/common/inject.ts +++ b/extension/js/common/inject.ts @@ -77,7 +77,7 @@ export class Injector { const secureComposeButton = $(this.factory.btnCompose(this.webmailName, this.determineWebmailVersion())); const insertBtnOnTopOf = this.S.now('compose_button_container').first(); const container = insertBtnOnTopOf.prepend(secureComposeButton); // xss-safe-factory - container.find(this.S.sel('compose_button')).click(Ui.event.prevent('double', () => { this.openComposeWin(); })); + container.find(this.S.sel('compose_button')).on('click', Ui.event.prevent('double', () => { this.openComposeWin(); })); } } }; @@ -94,7 +94,7 @@ export class Injector { } } prependToElem.prepend(this.factory.btnEndPPSession(this.webmailName)) // xss-safe-factory - .find('.action_finish_session').click(Ui.event.prevent('double', async (el) => { + .find('.action_finish_session').on('click', Ui.event.prevent('double', async (el) => { for (const kinfo of await KeyStore.getKeyInfosThatCurrentlyHavePassPhraseInSession(acctEmail)) { await PassphraseStore.set('session', acctEmail, kinfo, undefined); } diff --git a/extension/js/common/lang.ts b/extension/js/common/lang.ts index c4fc12a09c8..0ce86061442 100644 --- a/extension/js/common/lang.ts +++ b/extension/js/common/lang.ts @@ -74,6 +74,7 @@ export const Lang = { // tslint:disable-line:variable-name }, compose: { abortSending: 'A message is currently being sent. Closing the compose window may abort sending the message.\nAbort sending?', + addMissingRecipientPubkeys: `Some recipients don't have encryption set up. Please import their public keys or ask them to install Flowcrypt.`, pleaseReconnectAccount: 'Please reconnect FlowCrypt to your Gmail Account. This is typically needed after a long time of no use, a password change, or similar account changes. ', msgEncryptedHtml: (lang: string, senderEmail: string) => (lang === 'DE') ? `${senderEmail} hat Ihnen eine passwortverschlüsselte E-Mail gesendet ` : `${senderEmail} has sent you a password-encrypted email `, msgEncryptedText: (lang: string, senderEmail: string) => (lang === 'DE') ? `${senderEmail} hat Ihnen eine passwortverschlüsselte E-Mail gesendet. Folgen Sie diesem Link, um es zu öffnen: ` : `${senderEmail} has sent you a password-encrypted email. Follow this link to open it: `, @@ -103,4 +104,13 @@ export const Lang = { // tslint:disable-line:variable-name restartBrowserAndTryAgain: (isFesUsed: boolean) => `Unexpected error occured. Please restart your browser and try again. If this persists after a restart, ${contactForSupportSubsentence(isFesUsed)}.`, emailAliasChangedAskForReload: 'Your email aliases on Gmail have refreshed since the last time you used FlowCrypt.\nReload the compose window now?' }, + passphraseRequired: { + sign: 'Enter FlowCrypt pass phrase to sign email', + draft: 'Enter FlowCrypt pass phrase to load a draft', + attachment: 'Enter FlowCrypt pass phrase to decrypt a file', + quote: 'Enter FlowCrypt pass phrase to load quoted content', + backup: 'Enter FlowCrypt pass phrase to back up', + updateKey: 'Enter FlowCrypt pass phrase to keep your account keys up to date', + email: 'Enter FlowCrypt pass phrase to read encrypted email' + } }; diff --git a/extension/js/common/notifications.ts b/extension/js/common/notifications.ts index 81fce8f68b2..f42c7cf70ca 100644 --- a/extension/js/common/notifications.ts +++ b/extension/js/common/notifications.ts @@ -10,6 +10,7 @@ import { Catch } from './platform/catch.js'; import { AcctStore } from './platform/store/acct-store.js'; import { Xss } from './platform/xss.js'; +export type NotificationGroupType = 'setup' | 'notify_expiring_keys' | 'compose' | 'inbox'; export class Notifications { public showInitial = async (acctEmail: string) => { @@ -28,29 +29,37 @@ export class Notifications { }); }; - public clear = () => { - $('.webmail_notifications').text(''); + public clear = (group: NotificationGroupType) => { + $(`.${this.getNotificationGroupClass(group)}`)?.remove(); }; - public show = (text: string, callbacks: Dict<() => void> = {}) => { - Xss.sanitizeRender('.webmail_notifications', `
    ${text}
    `); + public show = (text: string, callbacks: Dict<() => void> = {}, group: NotificationGroupType = 'setup') => { + const notificationGroupClass = this.getNotificationGroupClass(group); + if ($(`.${notificationGroupClass}`).length < 1) { + Xss.sanitizePrepend('.webmail_notifications', `
    `); + } + Xss.sanitizeRender(`.${notificationGroupClass}`, text); if (typeof callbacks.close !== 'undefined') { const origCloseCb = callbacks.close; callbacks.close = Catch.try(() => { origCloseCb(); - this.clear(); + this.clear(group); }); } else { - callbacks.close = Catch.try(this.clear); + callbacks.close = Catch.try(() => this.clear(group)); } if (typeof callbacks.reload === 'undefined') { callbacks.reload = Catch.try(() => window.location.reload()); } for (const name of Object.keys(callbacks)) { - $(`.webmail_notifications .${name}`).click(Ui.event.prevent('double', callbacks[name])); + $(`.${notificationGroupClass} .${name}`).on('click', Ui.event.prevent('double', callbacks[name])); } }; + private getNotificationGroupClass = (group: NotificationGroupType) => { + return `webmail_notification_${group}_group`; + }; + private reconnectAcctAuthPopup = async (acctEmail: string) => { const authRes = await BrowserMsg.send.bg.await.reconnectAcctAuthPopup({ acctEmail }); if (authRes.result === 'Success') { diff --git a/extension/js/common/platform/store/acct-store.ts b/extension/js/common/platform/store/acct-store.ts index fdb13628102..ef5fc6d709c 100644 --- a/extension/js/common/platform/store/acct-store.ts +++ b/extension/js/common/platform/store/acct-store.ts @@ -1,15 +1,17 @@ /* ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact human@flowcrypt.com */ -import { Env } from '../../browser/env.js'; import { GoogleAuth } from '../../api/email-provider/gmail/google-auth.js'; -import { KeyInfoWithIdentity, StoredKeyInfo } from '../../core/crypto/key.js'; -import { Dict } from '../../core/common.js'; -import { ClientConfigurationJson } from '../../client-configuration.js'; -import { BrowserMsg, BgNotReadyErr } from '../../browser/browser-msg.js'; +import { ApiErr } from '../../api/shared/api-error.js'; +import { BgNotReadyErr, BrowserMsg } from '../../browser/browser-msg.js'; +import { storageLocalGet, storageLocalRemove, storageLocalSet } from '../../browser/chrome.js'; +import { Env } from '../../browser/env.js'; import { Ui } from '../../browser/ui.js'; -import { storageLocalGet, storageLocalSet, storageLocalRemove } from '../../browser/chrome.js'; -import { AbstractStore } from './abstract-store.js'; -import { RawStore } from './abstract-store.js'; +import { ClientConfigurationJson } from '../../client-configuration.js'; +import { Dict } from '../../core/common.js'; +import { InMemoryStoreKeys } from '../../core/const.js'; +import { KeyInfoWithIdentity, StoredKeyInfo } from '../../core/crypto/key.js'; +import { AbstractStore, RawStore } from './abstract-store.js'; +import { InMemoryStore } from './in-memory-store.js'; export type EmailProvider = 'gmail'; type GoogleAuthScopesNames = [keyof typeof GoogleAuth.OAUTH.scopes, keyof typeof GoogleAuth.OAUTH.legacy_scopes][number]; @@ -25,7 +27,7 @@ export type Scopes = { gmail: boolean; }; -export type AccountIndex = 'keys' | 'notification_setup_needed_dismissed' | 'email_provider' | 'google_token_scopes' | +export type AccountIndex = 'keys' | 'notification_setup_needed_dismissed' | 'email_provider' | 'google_token_refresh' | 'hide_message_password' | 'sendAs' | 'pubkey_sent_to' | 'full_name' | 'cryptup_enabled' | 'setup_done' | 'successfully_received_at_leat_one_message' | 'notification_setup_done_seen' | 'picture' | @@ -42,7 +44,6 @@ export type AcctStoreDict = { keys?: (StoredKeyInfo | KeyInfoWithIdentity)[]; // todo - migrate to KeyInfoWithIdentity only notification_setup_needed_dismissed?: boolean; email_provider?: EmailProvider; - google_token_scopes?: string[]; // these are actuall scope urls the way the provider expects them google_token_refresh?: string; hide_message_password?: boolean; // is global? sendAs?: Dict; @@ -125,19 +126,30 @@ export class AcctStore extends AbstractStore { }; public static getScopes = async (acctEmail: string): Promise => { - const { google_token_scopes } = await AcctStore.get(acctEmail, ['google_token_scopes']); + const accessToken = await InMemoryStore.get(acctEmail, InMemoryStoreKeys.GOOGLE_TOKEN_ACCESS); + // const { google_token_scopes } = await AcctStore.get(acctEmail, ['google_token_scopes']); const result: { [key in GoogleAuthScopesNames]: boolean } = { email: false, openid: false, profile: false, compose: false, modify: false, readContacts: false, readOtherContacts: false, gmail: false }; - if (google_token_scopes) { - for (const key of Object.keys({ ...GoogleAuth.OAUTH.scopes, ...GoogleAuth.OAUTH.legacy_scopes })) { - const scopeName = key as GoogleAuthScopesNames; - if (scopeName in GoogleAuth.OAUTH.scopes) { - result[scopeName] = google_token_scopes.includes(GoogleAuth.OAUTH.scopes[scopeName as keyof typeof GoogleAuth.OAUTH.scopes]); - } else if (scopeName in GoogleAuth.OAUTH.legacy_scopes) { - result[scopeName] = google_token_scopes.includes(GoogleAuth.OAUTH.legacy_scopes[scopeName as keyof typeof GoogleAuth.OAUTH.legacy_scopes]); - } + if (!accessToken) { + return result; + } + let allowedScopes: string[] = []; + try { + const { scope } = await GoogleAuth.getTokenInfo(accessToken); + allowedScopes = scope.split(' '); + } catch (e) { + if (ApiErr.isAuthErr(e)) { + BrowserMsg.send.notificationShowAuthPopupNeeded('broadcast', { acctEmail }); + } + } + for (const key of Object.keys({ ...GoogleAuth.OAUTH.scopes, ...GoogleAuth.OAUTH.legacy_scopes })) { + const scopeName = key as GoogleAuthScopesNames; + if (scopeName in GoogleAuth.OAUTH.scopes) { + result[scopeName] = allowedScopes.includes(GoogleAuth.OAUTH.scopes[scopeName as keyof typeof GoogleAuth.OAUTH.scopes]); + } else if (scopeName in GoogleAuth.OAUTH.legacy_scopes) { + result[scopeName] = allowedScopes.includes(GoogleAuth.OAUTH.legacy_scopes[scopeName as keyof typeof GoogleAuth.OAUTH.legacy_scopes]); } } return result; diff --git a/extension/js/common/platform/xss.ts b/extension/js/common/platform/xss.ts index 3989c0b6244..70c980909c2 100644 --- a/extension/js/common/platform/xss.ts +++ b/extension/js/common/platform/xss.ts @@ -98,16 +98,21 @@ export class Xss { } else if (!src) { img.remove(); // src that exists but is null is suspicious } else if (imgHandling === 'IMG-TO-LINK') { // replace images with a link that points to that image - const title = img.getAttribute('title'); - img.removeAttribute('src'); - const a = document.createElement('a'); - a.href = src; - a.className = 'image_src_link'; - a.target = '_blank'; - a.innerText = (title || 'show image') + (src.startsWith('data:image/') ? '' : ' (remote)'); - const heightWidth = `height: ${img.clientHeight ? `${Number(img.clientHeight)}px` : 'auto'}; width: ${img.clientWidth ? `${Number(img.clientWidth)}px` : 'auto'};max-width:98%;`; - a.setAttribute('style', `text-decoration: none; background: #FAFAFA; padding: 4px; border: 1px dotted #CACACA; display: inline-block; ${heightWidth}`); - Xss.replaceElementDANGEROUSLY(img, a.outerHTML); // xss-safe-value - "a" was build using dom node api + if (src.startsWith('data:image/')) { + const title = img.getAttribute('title'); + img.removeAttribute('src'); + const a = document.createElement('a'); + a.href = src; + a.className = 'image_src_link'; + a.target = '_blank'; + a.innerText = title || 'show image'; + const heightWidth = `height: ${img.clientHeight ? `${Number(img.clientHeight)}px` : 'auto'}; width: ${img.clientWidth ? `${Number(img.clientWidth)}px` : 'auto'};max-width:98%;`; + a.setAttribute('style', `text-decoration: none; background: #FAFAFA; padding: 4px; border: 1px dotted #CACACA; display: inline-block; ${heightWidth}`); + a.setAttribute('data-test', 'show-inline-image'); + Xss.replaceElementDANGEROUSLY(img, a.outerHTML); // xss-safe-value - "a" was build using dom node api + } else { + Xss.replaceElementDANGEROUSLY(img, `[Remote images are blocked due to security]`); // xss-safe-value + } } } if ('target' in node) { // open links in new window diff --git a/extension/js/common/settings.ts b/extension/js/common/settings.ts index d8cb5833879..141cdd7349d 100644 --- a/extension/js/common/settings.ts +++ b/extension/js/common/settings.ts @@ -25,6 +25,7 @@ import { KeyStore } from './platform/store/key-store.js'; import { PassphraseStore } from './platform/store/passphrase-store.js'; import { isFesUsed } from './helpers.js'; import { Api } from './api/shared/api.js'; +import { BrowserMsg } from './browser/browser-msg.js'; declare const zxcvbn: Function; // tslint:disable-line:ban-types @@ -211,7 +212,7 @@ export class Settings { } })); return await new Promise((resolve, reject) => { - container.find('.action_fix_compatibility').click(Ui.event.handle(async target => { + container.find('.action_fix_compatibility').on('click', Ui.event.handle(async target => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const expireYears = String($(target).parents(container as any).find('select.input_fix_expire_years').val()); // JQuery quirk if (!expireYears) { @@ -315,7 +316,7 @@ export class Settings { await Ui.modal.info(`${authDeniedHtml}\n
    ${Lang.general.contactIfNeedAssistance()}
    `, true); } else { // Do not report error for csrf - if (response.error !== 'Wrong oauth CSRF token. Please try again.') { + if (response.error !== 'Wrong oauth CSRF token. Please try again.' && !response.error?.includes('Missing client configuration flags')) { Catch.report('failed to log into google in newGoogleAcctAuthPromptThenAlertOrForward', response); } await Ui.modal.error(`Failed to connect to Gmail(new). ${Lang.general.contactIfHappensAgain(acctEmail ? @@ -356,7 +357,7 @@ export class Settings { $('#alt-accounts img.profile-img').on('error', Ui.event.handle(self => { $(self).off().attr('src', '/img/svgs/profile-icon.svg'); })); - $('.action_select_account').click(Ui.event.handle((target, event) => { + $('.action_select_account').on('click', Ui.event.handle((target, event) => { event.preventDefault(); const acctEmail = $(target).find('.contains_email').text(); const acctStorage = acctStorages[acctEmail]; @@ -380,7 +381,7 @@ export class Settings { await Ui.modal.info(`Reload after logging in.`); return window.location.reload(); } - const authRes = await GoogleAuth.newOpenidAuthPopup({ acctEmail }); + const authRes = await BrowserMsg.send.bg.await.reconnectAcctAuthPopup({ acctEmail }); if (authRes.result === 'Success' && authRes.acctEmail && authRes.id_token) { then(); } else { diff --git a/extension/js/common/ui/backup-ui/backup-ui-manual-module.ts b/extension/js/common/ui/backup-ui/backup-ui-manual-module.ts index 051962db021..118e3f61834 100644 --- a/extension/js/common/ui/backup-ui/backup-ui-manual-module.ts +++ b/extension/js/common/ui/backup-ui/backup-ui-manual-module.ts @@ -37,8 +37,8 @@ export class BackupUiManualActionModule extends BackupUiModule { } public setHandlers = () => { - $('#module_manual input[name=input_backup_choice]').click(this.ui.setHandler(el => this.actionSelectBackupMethodHandler(el))); - this.proceedBtn.click(this.ui.setHandlerPrevent('double', () => this.actionManualBackupHandler())); + $('#module_manual input[name=input_backup_choice]').on('click', this.ui.setHandler(el => this.actionSelectBackupMethodHandler(el))); + this.proceedBtn.on('click', this.ui.setHandlerPrevent('double', () => this.actionManualBackupHandler())); }; public doBackupOnEmailProvider = async (encryptedPrvs: KeyInfoWithIdentity[]) => { diff --git a/extension/js/common/ui/backup-ui/backup-ui-status-module.ts b/extension/js/common/ui/backup-ui/backup-ui-status-module.ts index 4bd48fb77ec..362e5ddba17 100644 --- a/extension/js/common/ui/backup-ui/backup-ui-status-module.ts +++ b/extension/js/common/ui/backup-ui/backup-ui-status-module.ts @@ -16,8 +16,8 @@ import { BackupUiModule } from './backup-ui-module.js'; export class BackupUiStatusModule extends BackupUiModule { public setHandlers = () => { // is run after checkAndRenderBackupStatus, which renders (some of) these fields first - $('#module_status .action_go_manual').click(this.ui.setHandler(() => this.actionShowManualBackupHandler())); - $('#module_status .action_go_add_key').click(this.ui.setHandler(async () => await this.goTo('add_key.htm'))); + $('#module_status .action_go_manual').on('click', this.ui.setHandler(() => this.actionShowManualBackupHandler())); + $('#module_status .action_go_add_key').on('click', this.ui.setHandler(async () => await this.goTo('add_key.htm'))); }; public checkAndRenderBackupStatus = async () => { diff --git a/extension/js/common/ui/backup-ui/backup-ui.ts b/extension/js/common/ui/backup-ui/backup-ui.ts index de7d411d72b..e4f8d8a2f6e 100644 --- a/extension/js/common/ui/backup-ui/backup-ui.ts +++ b/extension/js/common/ui/backup-ui/backup-ui.ts @@ -155,7 +155,7 @@ export class BackupUi { this.addKeyToBackup({ family: ki.family, id: ki.id }); } } - $('.input_prvkey_backup_checkbox').click(Ui.event.handle((target) => { + $('.input_prvkey_backup_checkbox').on('click', Ui.event.handle((target) => { const family = $(target).data('type') as string; if (family === 'openpgp') { const id = $(target).data('id') as string; diff --git a/extension/js/common/ui/key-import-ui.ts b/extension/js/common/ui/key-import-ui.ts index d785749aac7..05b3369c621 100644 --- a/extension/js/common/ui/key-import-ui.ts +++ b/extension/js/common/ui/key-import-ui.ts @@ -75,7 +75,7 @@ export class KeyImportUi { $('.input_private_key').val('').change().prop('disabled', true); $('.source_paste_container').css('display', 'none'); $('.source_paste_container .unprotected_key_create_pass_phrase').hide(); - $('#fineuploader_button > input').click(); + $('#fineuploader_button > input').trigger('click'); } else if ((this as HTMLInputElement).value === 'paste') { $('.input_private_key').val('').change().prop('disabled', false); $('.source_paste_container').css('display', 'block'); @@ -84,7 +84,7 @@ export class KeyImportUi { window.location.href = Url.create('/chrome/settings/setup.htm', { acctEmail, parentTabId, action: 'add_key' }); } }); - $('.line.unprotected_key_create_pass_phrase .action_use_random_pass_phrase').click(Ui.event.handle(() => { + $('.line.unprotected_key_create_pass_phrase .action_use_random_pass_phrase').on('click', Ui.event.handle(() => { $('.source_paste_container .input_passphrase').val(PgpPwd.random()).trigger('input'); $('.input_passphrase').attr('type', 'text'); $('#e_rememberPassphrase').prop('checked', true); diff --git a/extension/js/common/ui/passphrase-ui.ts b/extension/js/common/ui/passphrase-ui.ts index 5148a44fa47..dc8864d8326 100644 --- a/extension/js/common/ui/passphrase-ui.ts +++ b/extension/js/common/ui/passphrase-ui.ts @@ -33,9 +33,9 @@ export const initPassphraseToggle = async (passphraseInputIds: string[], forceIn passphraseInput.after(``); passphraseInput.attr('type', 'password'); } - $(`#toggle_${id}`).click(Ui.event.handle((target, event) => { + $(`#toggle_${id}`).on('click', Ui.event.handle((target, event) => { if (event.originalEvent) { - $('.toggle_show_hide_pass_phrase:visible').not(target).click(); // toggle the visibility of all other visible password fields on the page + $('.toggle_show_hide_pass_phrase:visible').not(target).trigger('click'); // toggle the visibility of all other visible password fields on the page } if (passphraseInput.attr('type') === 'password') { $(`#${id}`).attr('type', 'text'); @@ -46,6 +46,6 @@ export const initPassphraseToggle = async (passphraseInputIds: string[], forceIn Xss.sanitizeRender(target, buttonShow); GlobalStore.set({ hide_pass_phrases: true }).catch(Catch.reportErr); } - })).click().click(); // double-click the toggle to prevent browser from prefilling values + })).trigger('click').trigger('click'); // double-click the toggle to prevent browser from prefilling values } }; diff --git a/extension/js/common/view.ts b/extension/js/common/view.ts index de8a8b56a12..58041eccc03 100644 --- a/extension/js/common/view.ts +++ b/extension/js/common/view.ts @@ -57,7 +57,7 @@ export abstract class View { public setEnterHandlerThatClicks = (selector: string) => { return (event: JQuery.Event) => { if (event.which === 13) { - $(selector).click(); + $(selector).trigger('click'); } }; }; diff --git a/extension/js/content_scripts/webmail/gmail-element-replacer.ts b/extension/js/content_scripts/webmail/gmail-element-replacer.ts index d405651c9be..1bd19b2e3e5 100644 --- a/extension/js/content_scripts/webmail/gmail-element-replacer.ts +++ b/extension/js/content_scripts/webmail/gmail-element-replacer.ts @@ -179,7 +179,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { private addfcConvoIcon = (containerSel: JQueryEl, iconHtml: string, iconSel: string, onClick: () => void) => { containerSel.addClass('appended').children('.use_secure_reply, .show_original_conversation').remove(); // remove previous FlowCrypt buttons, if any - Xss.sanitizeAppend(containerSel, iconHtml).children(iconSel).off().click(Ui.event.prevent('double', Catch.try(onClick))); + Xss.sanitizeAppend(containerSel, iconHtml).children(iconSel).off().on('click', Ui.event.prevent('double', Catch.try(onClick))); }; private isEncrypted = (): boolean => { @@ -208,11 +208,11 @@ export class GmailElementReplacer implements WebmailElementReplacer { secureReplyBtn.on('focusout', Ui.event.handle((target) => { $(target).removeClass('T-I-JO'); })); secureReplyBtn.on('mouseenter', Ui.event.handle((target) => { $(target).addClass('T-I-JW'); })); secureReplyBtn.on('mouseleave', Ui.event.handle((target) => { $(target).removeClass('T-I-JW'); })); - secureReplyBtn.click(Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev))); + secureReplyBtn.on('click', Ui.event.handle((el, ev: JQuery.Event) => this.actionActivateSecureReplyHandler(el, ev))); secureReplyBtn.keydown(event => { if (event.key === 'Enter') { event.stopImmediatePropagation(); - $(secureReplyBtn).click(); + $(secureReplyBtn).trigger('click'); } }); } @@ -222,7 +222,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { if (useEncryptionInThisConvo) { if (!convoUpperIcons.is('.appended') || convoUpperIcons.find('.use_secure_reply').length) { // either not appended, or appended icon is outdated (convo switched to encrypted) this.addfcConvoIcon(convoUpperIcons, this.factory.btnWithoutFc(), '.show_original_conversation', () => { - convoUpperIcons.find('.gZ').click(); + convoUpperIcons.find('.gZ').trigger('click'); }); } } @@ -232,7 +232,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { private actionActivateSecureReplyHandler = async (btn: HTMLElement, event: JQuery.Event) => { event.stopImmediatePropagation(); if ($('#switch_to_encrypted_reply').length) { - $('#switch_to_encrypted_reply').click(); + $('#switch_to_encrypted_reply').trigger('click'); return; } const messageContainer = $(btn.closest('.h7') as HTMLElement); @@ -256,7 +256,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { const [, buttonHrefId] = draftLinkMatch; const button = `Open draft`; Xss.sanitizeReplace(contenteditable, button); - $(`a.open_draft_${buttonHrefId}`).click(Ui.event.handle((target) => { + $(`a.open_draft_${buttonHrefId}`).on('click', Ui.event.handle((target) => { if (this.injector.openComposeWin(buttonHrefId)) { closeGmailComposeWindow(target); } @@ -267,7 +267,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { mouseUpEvent.initEvent('mouseup', true, true); // Gmail listens for the mouseup event, not click $(target).closest('.dw').find('.Ha')[0].dispatchEvent(mouseUpEvent); // jquery's trigger('mouseup') doesn't work for some reason }; - $('.close_gmail_compose_window').click(Ui.event.handle(closeGmailComposeWindow)); + $('.close_gmail_compose_window').on('click', Ui.event.handle(closeGmailComposeWindow)); } } }; @@ -361,13 +361,14 @@ export class GmailElementReplacer implements WebmailElementReplacer { console.debug('processAttachments()', attachmentMetas); } let msgEl = this.getMsgBodyEl(msgId); // not a constant because sometimes elements get replaced, then returned by the function that replaced them + const isBodyEmpty = msgEl.text() === '' || msgEl.text() === '\n'; const senderEmail = this.getSenderEmail(msgEl); const isOutgoing = !!this.sendAs[senderEmail]; attachmentsContainerInner = $(attachmentsContainerInner); attachmentsContainerInner.parent().find(this.sel.numberOfAttachments).hide(); let nRenderedAttachments = attachmentMetas.length; for (const a of attachmentMetas) { - const treatAs = a.treatAs(); + const treatAs = a.treatAs(isBodyEmpty); // todo - [same name + not processed].first() ... What if attachment metas are out of order compared to how gmail shows it? And have the same name? const attachmentSel = this.filterAttachments(attachmentsContainerInner.children().not('.attachment_processed'), new RegExp(`^${Str.regexEscape(a.name || 'noname')}$`)).first(); if (this.debug) { @@ -664,7 +665,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { if (showSwitchToEncryptedReplyWarning) { const notification = $('
    The last message was encrypted, but you are composing a reply without encryption.
    '); const swithToEncryptedReply = $('Switch to encrypted reply'); - swithToEncryptedReply.click(Ui.event.handle((el, ev: JQuery.Event) => { + swithToEncryptedReply.on('click', Ui.event.handle((el, ev: JQuery.Event) => { ev.preventDefault(); $(el).closest('.reply_message_evaluated').removeClass('reply_message_evaluated'); this.removeNextReplyBoxBorders = true; @@ -728,7 +729,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { if (!standardComposeWin.find('.recipients_use_encryption').length) { const prependable = standardComposeWin.find('div.az9 span[email]').first().parents('form').first(); prependable.prepend(this.factory.btnRecipientsUseEncryption('gmail')); // xss-safe-factory - prependable.find('a').click(Ui.event.handle(() => { this.injector.openComposeWin(); })); + prependable.find('a').on('click', Ui.event.handle(() => { this.injector.openComposeWin(); })); } } else { standardComposeWin.find('.recipients_use_encryption').remove(); @@ -744,7 +745,7 @@ export class GmailElementReplacer implements WebmailElementReplacer { const settingsBtnContainer = $(this.sel.settingsBtnContainer); if (settingsBtnContainer.length && !settingsBtnContainer.find('#fc_settings_btn').length) { settingsBtnContainer.children().last().before(this.factory.btnSettings('gmail')); // xss-safe-factory - settingsBtnContainer.find('#fc_settings_btn').click(Ui.event.handle(() => BrowserMsg.send.bg.settings({ acctEmail: this.acctEmail }))); + settingsBtnContainer.find('#fc_settings_btn').on('click', Ui.event.handle(() => BrowserMsg.send.bg.settings({ acctEmail: this.acctEmail }))); } } }; diff --git a/extension/js/content_scripts/webmail/setup-webmail-content-script.ts b/extension/js/content_scripts/webmail/setup-webmail-content-script.ts index 913440722af..afac65aa070 100644 --- a/extension/js/content_scripts/webmail/setup-webmail-content-script.ts +++ b/extension/js/content_scripts/webmail/setup-webmail-content-script.ts @@ -2,26 +2,26 @@ 'use strict'; -import { Bm, BrowserMsg, TabIdRequiredError } from '../../common/browser/browser-msg.js'; -import { Env, WebMailName } from '../../common/browser/env.js'; -import { WebmailVariantString, XssSafeFactory } from '../../common/xss-safe-factory.js'; +import Swal from 'sweetalert2'; +import { AccountServer } from '../../common/api/account-server.js'; +import { KeyManager } from '../../common/api/key-server/key-manager.js'; +import { ApiErr, BackendAuthErr } from '../../common/api/shared/api-error.js'; import { BrowserMsgCommonHandlers } from '../../common/browser/browser-msg-common-handlers.js'; -import { Catch } from '../../common/platform/catch.js'; +import { Bm, BrowserMsg, TabIdRequiredError } from '../../common/browser/browser-msg.js'; import { ContentScriptWindow } from '../../common/browser/browser-window.js'; -import { Injector } from '../../common/inject.js'; -import { Notifications } from '../../common/notifications.js'; -import Swal from 'sweetalert2'; +import { Env, WebMailName } from '../../common/browser/env.js'; import { Ui } from '../../common/browser/ui.js'; +import { ClientConfiguration, ClientConfigurationError } from '../../common/client-configuration.js'; +import { Url } from '../../common/core/common.js'; import { InMemoryStoreKeys, VERSION } from '../../common/core/const.js'; +import { Injector } from '../../common/inject.js'; +import { Lang } from '../../common/lang.js'; +import { Notifications } from '../../common/notifications.js'; +import { Catch } from '../../common/platform/catch.js'; import { AcctStore } from '../../common/platform/store/acct-store.js'; import { GlobalStore } from '../../common/platform/store/global-store.js'; -import { ClientConfiguration } from '../../common/client-configuration.js'; -import { KeyManager } from '../../common/api/key-server/key-manager.js'; import { InMemoryStore } from '../../common/platform/store/in-memory-store.js'; -import { AccountServer } from '../../common/api/account-server.js'; -import { ApiErr, BackendAuthErr } from '../../common/api/shared/api-error.js'; -import { Url } from '../../common/core/common.js'; -import { Lang } from '../../common/lang.js'; +import { WebmailVariantString, XssSafeFactory } from '../../common/xss-safe-factory.js'; export type WebmailVariantObject = { newDataLayer: undefined | boolean, newUi: undefined | boolean, email: undefined | string, gmailVariant: WebmailVariantString }; export type IntervalFunction = { interval: number, handler: () => void }; @@ -105,11 +105,11 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi while (true) { const storage = await AcctStore.get(acctEmail, ['setup_done', 'cryptup_enabled', 'notification_setup_needed_dismissed']); if (storage.setup_done === true && storage.cryptup_enabled !== false) { // "not false" is due to cryptup_enabled unfedined in previous versions, which means "true" - notifications.clear(); + notifications.clear('setup'); return; } else if (!$("div.webmail_notification").length && !storage.notification_setup_needed_dismissed && showSetupNeededNotificationIfSetupNotDone && storage.cryptup_enabled !== false) { notifications.show(setUpNotification, { - notification_setup_needed_dismiss: () => AcctStore.set(acctEmail, { notification_setup_needed_dismissed: true }).then(() => notifications.clear()).catch(Catch.reportErr), + notification_setup_needed_dismiss: () => AcctStore.set(acctEmail, { notification_setup_needed_dismissed: true }).then(() => notifications.clear('setup')).catch(Catch.reportErr), action_open_settings: () => BrowserMsg.send.bg.settings({ acctEmail }), close: () => { showSetupNeededNotificationIfSetupNotDone = false; @@ -190,9 +190,9 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi BrowserMsg.addListener('add_pubkey_dialog', async ({ emails }: Bm.AddPubkeyDialog) => { await factory.showAddPubkeyDialog(emails); }); - BrowserMsg.addListener('notification_show', async ({ notification, callbacks }: Bm.NotificationShow) => { - notifications.show(notification, callbacks); - $('body').one('click', Catch.try(notifications.clear)); + BrowserMsg.addListener('notification_show', async ({ notification, callbacks, group }: Bm.NotificationShow) => { + notifications.show(notification, callbacks, group); + $('body').one('click', Catch.try(() => notifications.clear(group))); }); BrowserMsg.addListener('notification_show_auth_popup_needed', async ({ acctEmail }: Bm.NotificationShowAuthPopupNeeded) => { notifications.showAuthPopupNeeded(acctEmail); @@ -286,16 +286,30 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi } }; - const startPullingKeysFromEkm = async (acctEmail: string, clientConfiguration: ClientConfiguration, factory: XssSafeFactory, ppEvent: { entered?: boolean }) => { + const startPullingKeysFromEkm = async ( + acctEmail: string, + clientConfiguration: ClientConfiguration, + factory: XssSafeFactory, + ppEvent: { entered?: boolean }, + completion: () => void + ) => { if (clientConfiguration.usesKeyManager()) { const idToken = await InMemoryStore.get(acctEmail, InMemoryStoreKeys.ID_TOKEN); if (idToken) { const keyManager = new KeyManager(clientConfiguration.getKeyManagerUrlForPrivateKeys()!); Catch.setHandledTimeout(async () => { - const { privateKeys } = await keyManager.getPrivateKeys(idToken); - await processKeysFromEkm(acctEmail, privateKeys.map(entry => entry.decryptedPrivateKey), clientConfiguration, factory, idToken, ppEvent); + try { + const { privateKeys } = await keyManager.getPrivateKeys(idToken); + await processKeysFromEkm(acctEmail, privateKeys.map(entry => entry.decryptedPrivateKey), clientConfiguration, factory, idToken, ppEvent); + } finally { + completion(); + } }, 0); + } else { + completion(); } + } else { + completion(); } }; @@ -308,6 +322,8 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi // at which point the update will happen next time user loads the page } else if (ApiErr.isNetErr(e)) { // ignore + } else if (e instanceof ClientConfigurationError) { + Ui.toast(`Failed to update FlowCrypt Client Configuration: ${e.message}`, false, 5); } else { Catch.reportErr(e); // tslint:disable-next-line:no-unsafe-any @@ -318,6 +334,27 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi }; + const notifyExpiringKeys = async (acctEmail: string, clientConfiguration: ClientConfiguration, notifications: Notifications) => { + const expiration = await BrowserMsg.send.bg.await.getLocalKeyExpiration({ acctEmail }); + if (expiration === undefined) { + return; + } + const expireInDays = Math.ceil((expiration - Date.now()) / 1000 / 60 / 60 / 24); + if (expireInDays > 30) { + return; + } + let warningMsg; + if (clientConfiguration.usesKeyManager()) { + warningMsg = `Your local keys expire in ${expireInDays} days.
    ` + + `To receive the latest keys, please ensure that you can connect to your corporate network either through VPN or in person and reload Gmail.
    ` + + `If this notification still shows after that, please contact your Help Desk.`; + } else { + warningMsg = `Your keys are expiring in ${expireInDays} days. Please import a newer set of keys to use.`; + } + warningMsg += `close`; + notifications.show(warningMsg, {}, 'notify_expiring_keys'); + }; + const entrypoint = async () => { try { const acctEmail = await waitForAcctEmail(); @@ -327,7 +364,7 @@ export const contentScriptSetupIfVacant = async (webmailSpecific: WebmailSpecifi const ppEvent: { entered?: boolean } = {}; browserMsgListen(acctEmail, tabId, inject, factory, notifications, ppEvent); const clientConfiguration = await ClientConfiguration.newInstance(acctEmail); - await startPullingKeysFromEkm(acctEmail, clientConfiguration, factory, ppEvent); + await startPullingKeysFromEkm(acctEmail, clientConfiguration, factory, ppEvent, Catch.try(() => notifyExpiringKeys(acctEmail, clientConfiguration, notifications))); await webmailSpecific.start(acctEmail, clientConfiguration, inject, notifications, factory, notifyMurdered); } catch (e) { if (e instanceof TabIdRequiredError) { diff --git a/package-lock.json b/package-lock.json index bb567d7df81..0cffa19b1a3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,61 +1,61 @@ { "name": "flowcrypt-browser", - "version": "8.3.8", + "version": "8.4.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "flowcrypt-browser", - "version": "8.3.8", + "version": "8.4.0", "license": "SEE LICENSE IN ", "dependencies": { "@flowcrypt/fine-uploader": "5.16.4", "bootstrap": "4.6.2", "clipboard": "2.0.11", - "dompurify": "2.4.0", + "dompurify": "2.4.1", "filesize": "10.0.5", "iso-8859-2": "1.0.0", "jquery": "3.6.1", "node-forge": "1.3.1", "postcss-html": "^1.5.0", "squire-rte": "1.11.3", - "sweetalert2": "11.4.33", + "sweetalert2": "11.6.8", "zxcvbn": "4.4.2" }, "devDependencies": { "@openpgp/web-stream-tools": "^0.0.11", - "@types/chai": "4.3.3", + "@types/chai": "4.3.4", "@types/chai-as-promised": "7.1.5", - "@types/chrome": "0.0.199", - "@types/dompurify": "2.3.4", + "@types/chrome": "0.0.203", + "@types/dompurify": "2.4.0", "@types/jquery": "3.5.14", "@types/mailparser": "3.4.0", "@types/mkdirp": "1.0.2", "@types/request": "2.48.8", - "@typescript-eslint/eslint-plugin": "5.40.1", - "@typescript-eslint/parser": "5.40.1", - "ava": "5.0.1", - "chai": "4.3.6", + "@typescript-eslint/eslint-plugin": "5.45.0", + "@typescript-eslint/parser": "5.45.0", + "ava": "5.1.0", + "chai": "4.3.7", "chai-as-promised": "7.1.1", "del": "7.0.0", - "eslint": "8.24.0", + "eslint": "8.28.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-no-only-tests": "3.1.0", "fc-node-requests": "git+https://git@github.com/FlowCrypt/node-requests.git", - "googleapis": "108.0.0", - "husky": "^8.0.1", - "lint-staged": "^13.0.3", + "googleapis": "109.0.1", + "husky": "^8.0.2", + "lint-staged": "^13.0.4", "mailparser": "3.5.0", "mkdirp": "1.0.4", "openpgp": ">=5.5.0", - "pdfjs-dist": "2.16.105", - "puppeteer": "19.1.0", - "stylelint": "14.14.0", + "pdfjs-dist": "3.1.81", + "puppeteer": "19.3.0", + "stylelint": "14.15.0", "stylelint-config-standard": "29.0.0", "tslint": "6.1.3", "tsutils": "^3.21.0", - "typescript": "4.8.4", - "web-ext": "7.3.1" + "typescript": "4.9.4", + "web-ext": "7.4.0" } }, "node_modules/@babel/code-frame": { @@ -94,12 +94,12 @@ } }, "node_modules/@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "dev": true, "dependencies": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" }, "engines": { "node": ">=6.9.0" @@ -216,30 +216,6 @@ "resolved": "https://registry.npmjs.org/@flowcrypt/fine-uploader/-/fine-uploader-5.16.4.tgz", "integrity": "sha512-m4jVj6ZNlG2LK+KPjQfJBIXnhn3TC2o/RdkBzj2a0XIyEX9DfjgqeZhUdW6d276DG4vAriLDN/4Ezz545AA7TA==" }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz", - "integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==", - "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - }, - "engines": { - "node": ">=10.10.0" - } - }, - "node_modules/@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", - "dev": true, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/nzakas" - } - }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -268,19 +244,40 @@ "node": ">= 12" } }, + "node_modules/@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "dev": true, + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + }, + "bin": { + "node-pre-gyp": "bin/node-pre-gyp" + } + }, "node_modules/@mdn/browser-compat-data": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.2.6.tgz", - "integrity": "sha512-KJfP6iTcVX+R5OSC4NOIF4V9fTyifcjwmdkOk7UzsaWxkF21rc6KhGlohqiSRVEynidGO1EEyyYf/PD3jsM1gA==", + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.2.17.tgz", + "integrity": "sha512-aA+rFHhXmq14GVIcEWNk8OntLEOQFwEZk9ZgG5VcDquz+pQhIjJPXacR+rwL9Z0Elfg909EcRRHC96p06/CNUg==", "dev": true }, "node_modules/@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "dependencies": { - "@nodelib/fs.stat": "2.0.3", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" }, "engines": { @@ -288,21 +285,21 @@ } }, "node_modules/@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true, "engines": { "node": ">= 8" } }, "node_modules/@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "dependencies": { - "@nodelib/fs.scandir": "2.1.3", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { @@ -409,9 +406,9 @@ "dev": true }, "node_modules/@types/chai": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", - "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, "node_modules/@types/chai-as-promised": { @@ -424,9 +421,9 @@ } }, "node_modules/@types/chrome": { - "version": "0.0.199", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.199.tgz", - "integrity": "sha512-BDuKiS8iZONsvTFSjbJlmHAwkYpkcWtG7Z7ESDJ/vf0QlcaXX6q4Xzi7euDNjIS4ZMA4kSODPGBHGVdC2lAHzw==", + "version": "0.0.203", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.203.tgz", + "integrity": "sha512-JlQNebwpBETVc8U1Rr2inDFuOTtn0lahRAhnddx1dd0S5RrLAFJEEsyIu7AXI14mkCgSunksnuLGioH8kvBqRA==", "dev": true, "dependencies": { "@types/filesystem": "*", @@ -434,9 +431,9 @@ } }, "node_modules/@types/dompurify": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.4.tgz", - "integrity": "sha512-EXzDatIb5EspL2eb/xPGmaC8pePcTHrkDCONjeisusLFrVfl38Pjea/R0YJGu3k9ZQadSvMqW0WXPI2hEo2Ajg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", + "integrity": "sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg==", "dev": true, "dependencies": { "@types/trusted-types": "*" @@ -570,9 +567,9 @@ } }, "node_modules/@types/semver": { - "version": "7.3.12", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz", - "integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==", + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, "node_modules/@types/sizzle": { @@ -603,16 +600,17 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.40.1.tgz", - "integrity": "sha512-FsWboKkWdytGiXT5O1/R9j37YgcjO8MKHSUmWnIEjVaz0krHkplPnYi7mwdb+5+cs0toFNQb0HIrN7zONdIEWg==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz", + "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/type-utils": "5.40.1", - "@typescript-eslint/utils": "5.40.1", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/type-utils": "5.45.0", + "@typescript-eslint/utils": "5.45.0", "debug": "^4.3.4", "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" @@ -635,14 +633,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.40.1.tgz", - "integrity": "sha512-IK6x55va5w4YvXd4b3VrXQPldV9vQTxi5ov+g4pMANsXPTXOcfjx08CRR1Dfrcc51syPtXHF5bgLlMHYFrvQtg==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz", + "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/typescript-estree": "5.40.1", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", "debug": "^4.3.4" }, "engines": { @@ -662,13 +660,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.40.1.tgz", - "integrity": "sha512-jkn4xsJiUQucI16OLCXrLRXDZ3afKhOIqXs4R3O+M00hdQLKR58WuyXPZZjhKLFCEP2g+TXdBRtLQ33UfAdRUg==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz", + "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1" + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -679,13 +677,13 @@ } }, "node_modules/@typescript-eslint/type-utils": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.40.1.tgz", - "integrity": "sha512-DLAs+AHQOe6n5LRraXiv27IYPhleF0ldEmx6yBqBgBLaNRKTkffhV1RPsjoJBhVup2zHxfaRtan8/YRBgYhU9Q==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz", + "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==", "dev": true, "dependencies": { - "@typescript-eslint/typescript-estree": "5.40.1", - "@typescript-eslint/utils": "5.40.1", + "@typescript-eslint/typescript-estree": "5.45.0", + "@typescript-eslint/utils": "5.45.0", "debug": "^4.3.4", "tsutils": "^3.21.0" }, @@ -706,9 +704,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.40.1.tgz", - "integrity": "sha512-Icg9kiuVJSwdzSQvtdGspOlWNjVDnF3qVIKXdJ103o36yRprdl3Ge5cABQx+csx960nuMF21v8qvO31v9t3OHw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz", + "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==", "dev": true, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" @@ -719,13 +717,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.1.tgz", - "integrity": "sha512-5QTP/nW5+60jBcEPfXy/EZL01qrl9GZtbgDZtDPlfW5zj/zjNrdI2B5zMUHmOsfvOr2cWqwVdWjobCiHcedmQA==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz", + "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -746,16 +744,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.40.1.tgz", - "integrity": "sha512-a2TAVScoX9fjryNrW6BZRnreDUszxqm9eQ9Esv8n5nXApMW0zeANUYlwh/DED04SC/ifuBvXgZpIK5xeJHQ3aw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz", + "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/typescript-estree": "5.40.1", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" @@ -772,12 +770,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.1.tgz", - "integrity": "sha512-A2DGmeZ+FMja0geX5rww+DpvILpwo1OsiQs0M+joPWJYsiEFBLsH0y1oFymPNul6Z5okSmHpP4ivkc2N0Cgfkw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz", + "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==", "dev": true, "dependencies": { - "@typescript-eslint/types": "5.40.1", + "@typescript-eslint/types": "5.45.0", "eslint-visitor-keys": "^3.3.0" }, "engines": { @@ -797,6 +795,13 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "optional": true + }, "node_modules/abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -810,9 +815,9 @@ } }, "node_modules/acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true, "bin": { "acorn": "bin/acorn" @@ -840,25 +845,25 @@ } }, "node_modules/addons-linter": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-5.18.0.tgz", - "integrity": "sha512-pce7nSuf/fNesDTmiD077auB15gcWZVHSVFmmAU/mm4BpzDPJBYp5rBYVMDaLjTAsYxR6Qq1ICBN8zryso2UxQ==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-5.23.0.tgz", + "integrity": "sha512-Vo6+5YlM2Ge3yYMY+gNg9Smcfcl1J0ZMfGVXnGJjUwDVHuszHVIvurunQuJURnO4FR1gi4Vy1sWye8ArRL5LOw==", "dev": true, "dependencies": { - "@mdn/browser-compat-data": "5.2.6", - "addons-moz-compare": "1.2.0", + "@mdn/browser-compat-data": "5.2.17", + "addons-moz-compare": "1.3.0", "addons-scanner-utils": "8.1.0", - "ajv": "8.11.0", + "ajv": "8.11.2", "ajv-merge-patch": "5.0.1", "chalk": "4.1.2", "cheerio": "1.0.0-rc.12", "columnify": "1.6.0", "common-tags": "1.8.2", "deepmerge": "4.2.2", - "eslint": "8.25.0", + "eslint": "8.28.0", "eslint-plugin-no-unsanitized": "4.0.1", "eslint-visitor-keys": "3.3.0", - "espree": "9.4.0", + "espree": "9.4.1", "esprima": "4.0.1", "fluent-syntax": "0.13.0", "glob": "8.0.3", @@ -866,15 +871,15 @@ "is-mergeable-object": "1.1.1", "jed": "1.1.1", "os-locale": "5.0.0", - "pino": "8.6.1", - "postcss": "8.4.18", + "pino": "8.7.0", + "postcss": "8.4.19", "relaxed-json": "1.0.3", "semver": "7.3.8", "sha.js": "2.4.11", "source-map-support": "0.5.21", "tosource": "1.0.0", "upath": "2.0.1", - "yargs": "17.6.0", + "yargs": "17.6.2", "yauzl": "2.10.0" }, "bin": { @@ -885,9 +890,9 @@ } }, "node_modules/addons-linter/node_modules/ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -928,12 +933,6 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/addons-linter/node_modules/argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "node_modules/addons-linter/node_modules/brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -977,86 +976,6 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "node_modules/addons-linter/node_modules/escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/addons-linter/node_modules/eslint": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz", - "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==", - "dev": true, - "dependencies": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/module-importer": "^1.0.1", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "bin": { - "eslint": "bin/eslint.js" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/eslint" - } - }, - "node_modules/addons-linter/node_modules/eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "dependencies": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - } - }, "node_modules/addons-linter/node_modules/eslint-visitor-keys": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", @@ -1066,53 +985,6 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "node_modules/addons-linter/node_modules/eslint/node_modules/ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "dependencies": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/epoberezkin" - } - }, - "node_modules/addons-linter/node_modules/eslint/node_modules/json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - }, - "node_modules/addons-linter/node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/addons-linter/node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/addons-linter/node_modules/glob": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", @@ -1132,121 +1004,28 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/addons-linter/node_modules/glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.3" - }, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/addons-linter/node_modules/glob/node_modules/minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/addons-linter/node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/addons-linter/node_modules/json-schema-traverse": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", "dev": true }, - "node_modules/addons-linter/node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "dependencies": { - "p-locate": "^5.0.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/addons-linter/node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/addons-linter/node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "node_modules/addons-linter/node_modules/minimatch": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", + "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", "dev": true, "dependencies": { - "p-limit": "^3.0.2" + "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/addons-linter/node_modules/strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", - "dev": true, - "dependencies": { - "ansi-regex": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/addons-linter/node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/addons-moz-compare": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.2.0.tgz", - "integrity": "sha512-COG8qk2/dubPqabfcoJW4E7pm2EQDI43iMrHnhlobvq/uRMEzx/PYJ1KaUZ97Vgg44R3QdRG5CvDsTRbMUHcDw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.3.0.tgz", + "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==", "dev": true }, "node_modules/addons-scanner-utils": { @@ -1317,15 +1096,6 @@ "node": ">=8" } }, - "node_modules/aggregate-error/node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", @@ -1418,6 +1188,27 @@ "node": ">= 8" } }, + "node_modules/aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true, + "optional": true + }, + "node_modules/are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "optional": true, + "dependencies": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -1548,22 +1339,22 @@ } }, "node_modules/ava": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ava/-/ava-5.0.1.tgz", - "integrity": "sha512-nS1eK3HhWaC+eGHtteF5j4yZMjaIE+q2o+oyqD75xsmS87R5sGlxADYWkFIGyB28jrDmAATZAAx+s3JhYsnhNw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ava/-/ava-5.1.0.tgz", + "integrity": "sha512-e5VFrSQ0WBPyZJWRXVrO7RFOizFeNM0t2PORwrPvWtApgkORI6cvGnY3GX1G+lzpd0HjqNx5Jus22AhxVnUMNA==", "dev": true, "dependencies": { - "acorn": "^8.8.0", + "acorn": "^8.8.1", "acorn-walk": "^8.2.0", - "ansi-styles": "^6.1.1", + "ansi-styles": "^6.2.1", "arrgv": "^1.0.2", "arrify": "^3.0.0", "callsites": "^4.0.0", "cbor": "^8.1.0", - "chalk": "^5.0.1", + "chalk": "^5.1.2", "chokidar": "^3.5.3", "chunkd": "^2.0.1", - "ci-info": "^3.4.0", + "ci-info": "^3.6.1", "ci-parallel-vars": "^1.0.1", "clean-yaml-object": "^0.1.0", "cli-truncate": "^3.1.0", @@ -1573,7 +1364,7 @@ "currently-unhandled": "^0.4.1", "debug": "^4.3.4", "del": "^7.0.0", - "emittery": "^1.0.0", + "emittery": "^1.0.1", "figures": "^5.0.0", "globby": "^13.1.2", "ignore-by-default": "^2.1.0", @@ -1592,12 +1383,12 @@ "pretty-ms": "^8.0.0", "resolve-cwd": "^3.0.0", "slash": "^3.0.0", - "stack-utils": "^2.0.5", + "stack-utils": "^2.0.6", "strip-ansi": "^7.0.1", "supertap": "^3.0.1", - "temp-dir": "^2.0.0", - "write-file-atomic": "^4.0.2", - "yargs": "^17.5.1" + "temp-dir": "^3.0.0", + "write-file-atomic": "^5.0.0", + "yargs": "^17.6.2" }, "bin": { "ava": "entrypoints/cli.mjs" @@ -1651,9 +1442,9 @@ } }, "node_modules/ava/node_modules/chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", "dev": true, "engines": { "node": "^12.17.0 || ^14.13 || >=16.0.0" @@ -1740,16 +1531,16 @@ "dev": true }, "node_modules/ava/node_modules/write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" }, "engines": { - "node": "^12.13.0 || ^14.15.0 || >=16.0.0" + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/aws-sign2": { @@ -2142,6 +1933,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/canvas": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.2.tgz", + "integrity": "sha512-FSmlsip0nZ0U4Zcfht0qBJqDhlfGuevTZKE8h+dBOYrJjGvY3iqMGSzzbvkaFhvMXiVxfcMaPHS/kge++T5SKg==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -2161,14 +1968,14 @@ } }, "node_modules/chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "dev": true, "dependencies": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", - "deep-eql": "^3.0.1", + "deep-eql": "^4.1.2", "get-func-name": "^2.0.0", "loupe": "^2.3.1", "pathval": "^1.1.1", @@ -2419,10 +2226,13 @@ "dev": true }, "node_modules/ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", - "dev": true + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.2.tgz", + "integrity": "sha512-lVZdhvbEudris15CLytp2u6Y0p5EKfztae9Fqa189MfNmln9F33XuH69v5fvNfiRN5/0eAUz2yJL3mo+nhaRKg==", + "dev": true, + "engines": { + "node": ">=8" + } }, "node_modules/ci-parallel-vars": { "version": "1.0.1", @@ -2589,6 +2399,16 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "node_modules/color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "optional": true, + "bin": { + "color-support": "bin.js" + } + }, "node_modules/colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -2768,6 +2588,13 @@ "typedarray-to-buffer": "^3.1.5" } }, + "node_modules/console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "optional": true + }, "node_modules/convert-to-spaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", @@ -3094,15 +2921,15 @@ } }, "node_modules/deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.2.tgz", + "integrity": "sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==", "dev": true, "dependencies": { "type-detect": "^4.0.0" }, "engines": { - "node": ">=0.12" + "node": ">=6" } }, "node_modules/deep-extend": { @@ -3235,10 +3062,27 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, + "node_modules/delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "optional": true + }, + "node_modules/detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, "node_modules/devtools-protocol": { - "version": "0.0.1045489", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1045489.tgz", - "integrity": "sha512-D+PTmWulkuQW4D1NTiCRCFxF7pQPn0hgp4YyX4wAQ6xYXKOadSWPR3ENGDQ47MW/Ewc9v2rpC/UEEGahgBYpSQ==", + "version": "0.0.1056733", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1056733.tgz", + "integrity": "sha512-CmTu6SQx2g3TbZzDCAV58+LTxVdKplS7xip0g5oDXpZ+isr0rv5dDP8ToyVRywzPHkCCPKgKgScEcwz4uPWDIA==", "dev": true }, "node_modules/diff": { @@ -3329,16 +3173,10 @@ "url": "https://github.com/fb55/domhandler?sponsor=1" } }, - "node_modules/dommatrix": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-1.0.3.tgz", - "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==", - "dev": true - }, "node_modules/dompurify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz", - "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz", + "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==" }, "node_modules/domutils": { "version": "2.8.0", @@ -3409,9 +3247,9 @@ } }, "node_modules/emittery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.0.tgz", - "integrity": "sha512-TD/u5aAn5W2HI2OukSIReNYXf/cH7U0QZHPxM4aIVYy0CmtrLCvf+7E8MuV2BbO02l6wq4sAKuFA8H16l6AHMA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.1.tgz", + "integrity": "sha512-2ID6FdrMD9KDLldGesP6317G78K7km/kMcwItRtVFva7I/cSEOIaLpewaUb+YLXVwdAp3Ctfxh/V5zIl1sj7dQ==", "dev": true, "engines": { "node": ">=14.16" @@ -3510,15 +3348,15 @@ } }, "node_modules/eslint": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", + "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", "dev": true, "dependencies": { - "@eslint/eslintrc": "^1.3.2", - "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -3534,14 +3372,14 @@ "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "glob-parent": "^6.0.1", + "glob-parent": "^6.0.2", "globals": "^13.15.0", - "globby": "^11.1.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", @@ -3632,6 +3470,20 @@ "node": ">=10" } }, + "node_modules/eslint/node_modules/@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } + }, "node_modules/eslint/node_modules/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -3758,6 +3610,15 @@ "node": ">=10.13.0" } }, + "node_modules/eslint/node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, "node_modules/eslint/node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -3840,9 +3701,9 @@ } }, "node_modules/espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dev": true, "dependencies": { "acorn": "^8.8.0", @@ -4370,6 +4231,19 @@ "node": ">= 10.0.0" } }, + "node_modules/fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "optional": true, + "dependencies": { + "minipass": "^3.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -4430,6 +4304,40 @@ "which": "bin/which" } }, + "node_modules/gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "optional": true, + "dependencies": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/gauge/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "optional": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/gaxios": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.0.tgz", @@ -4730,9 +4638,9 @@ } }, "node_modules/googleapis": { - "version": "108.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-108.0.0.tgz", - "integrity": "sha512-wQuBzCObtjpfg3CksOfUlX3yT8clw/vJFdGSfs9cpn84WSxNK3U5sxYxEH3mPM+d+SrA8znKM9G8sOuwQceGIA==", + "version": "109.0.1", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-109.0.1.tgz", + "integrity": "sha512-x286OtNu0ngzxfGz2XgRs4aMhrwutRCkCE12dh2M1jIZOpOndB7ELFXEhmtxaJ7z3257flKIbiiCJZeBO+ze/Q==", "dev": true, "dependencies": { "google-auth-library": "^8.0.2", @@ -4969,6 +4877,13 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "optional": true + }, "node_modules/has-yarn": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", @@ -5189,9 +5104,9 @@ } }, "node_modules/husky": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", - "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.2.tgz", + "integrity": "sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==", "dev": true, "bin": { "husky": "lib/bin.js" @@ -5676,9 +5591,9 @@ "dev": true }, "node_modules/jose": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.10.0.tgz", - "integrity": "sha512-KEhB/eLGLomWGPTb+/RNbYsTjIyx03JmbqAyIyiXBuNSa7CmNrJd5ysFhblayzs/e/vbOPMUaLnjHUMhGp4yLw==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", + "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==", "dev": true, "funding": { "url": "https://github.com/sponsors/panva" @@ -5918,9 +5833,9 @@ } }, "node_modules/known-css-properties": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", - "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", "dev": true }, "node_modules/latest-version": { @@ -6022,9 +5937,9 @@ "dev": true }, "node_modules/lilconfig": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", - "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true, "engines": { "node": ">=10" @@ -6046,24 +5961,24 @@ } }, "node_modules/lint-staged": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz", - "integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==", + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.4.tgz", + "integrity": "sha512-HxlHCXoYRsq9QCby5wFozmZW00hMs/9e3l+/dz6Qr8Kle4UH0kJTdABAbqhzG+3pcG6QjL9kz7NgGBfph+a5dw==", "dev": true, "dependencies": { "cli-truncate": "^3.1.0", - "colorette": "^2.0.17", - "commander": "^9.3.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", "debug": "^4.3.4", "execa": "^6.1.0", - "lilconfig": "2.0.5", - "listr2": "^4.0.5", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-inspect": "^1.12.2", "pidtree": "^0.6.0", "string-argv": "^0.3.1", - "yaml": "^2.1.1" + "yaml": "^2.1.3" }, "bin": { "lint-staged": "bin/lint-staged.js" @@ -6076,9 +5991,9 @@ } }, "node_modules/lint-staged/node_modules/commander": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz", - "integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", "dev": true, "engines": { "node": "^12.20.0 || >=14" @@ -6207,31 +6122,31 @@ } }, "node_modules/lint-staged/node_modules/yaml": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", - "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", "dev": true, "engines": { "node": ">= 14" } }, "node_modules/listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz", + "integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==", "dev": true, "dependencies": { "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", + "colorette": "^2.0.19", "log-update": "^4.0.0", "p-map": "^4.0.0", "rfdc": "^1.3.0", - "rxjs": "^7.5.5", + "rxjs": "^7.5.7", "through": "^2.3.8", "wrap-ansi": "^7.0.0" }, "engines": { - "node": ">=12" + "node": "^14.13.1 || >=16.0.0" }, "peerDependencies": { "enquirer": ">= 2.3.0 < 3" @@ -6410,7 +6325,7 @@ "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "node_modules/log-update": { @@ -6577,6 +6492,32 @@ "libqp": "1.1.0" } }, + "node_modules/make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "optional": true, + "dependencies": { + "semver": "^6.0.0" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/make-dir/node_modules/semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "optional": true, + "bin": { + "semver": "bin/semver.js" + } + }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -6828,6 +6769,33 @@ "node": ">= 6" } }, + "node_modules/minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "optional": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "optional": true, + "dependencies": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -6968,9 +6936,9 @@ } }, "node_modules/nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", "dev": true, "optional": true }, @@ -6991,6 +6959,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "node_modules/ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", @@ -7117,6 +7091,22 @@ "node": ">=12.19" } }, + "node_modules/nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "optional": true, + "dependencies": { + "abbrev": "1" + }, + "bin": { + "nopt": "bin/nopt.js" + }, + "engines": { + "node": ">=6" + } + }, "node_modules/normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -7165,6 +7155,19 @@ "node": ">=8" } }, + "node_modules/npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "optional": true, + "dependencies": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "node_modules/nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -7533,9 +7536,9 @@ } }, "node_modules/parse5": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz", - "integrity": "sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", "dev": true, "dependencies": { "entities": "^4.4.0" @@ -7637,21 +7640,15 @@ } }, "node_modules/pdfjs-dist": { - "version": "2.16.105", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz", - "integrity": "sha512-J4dn41spsAwUxCpEoVf6GVoz908IAA3mYiLmNxg8J9kfRXc2jxpbUepcP0ocp0alVNLFthTAM8DZ1RaHh8sU0A==", + "version": "3.1.81", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.1.81.tgz", + "integrity": "sha512-hZHVVbjU2Ac1VYyPFrg9fBcyS7EEdB8YFy5upk6LmnsXl10WxAavdiViGWi2C/xK0GZObEpSSJU1VnoF9t8n9w==", "dev": true, "dependencies": { - "dommatrix": "^1.0.3", "web-streams-polyfill": "^3.2.1" }, - "peerDependencies": { - "worker-loader": "^3.0.8" - }, - "peerDependenciesMeta": { - "worker-loader": { - "optional": true - } + "optionalDependencies": { + "canvas": "^2.10.2" } }, "node_modules/pend": { @@ -7696,9 +7693,9 @@ } }, "node_modules/pino": { - "version": "8.6.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.6.1.tgz", - "integrity": "sha512-fi+V2K98eMZjQ/uEHHSiMALNrz7HaFdKNYuyA3ZUrbH0f1e8sPFDmeRGzg7ZH2q4QDxGnJPOswmqlEaTAZeDPA==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.7.0.tgz", + "integrity": "sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0", @@ -7885,9 +7882,9 @@ } }, "node_modules/postcss": { - "version": "8.4.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", - "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", + "version": "8.4.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", + "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", "funding": [ { "type": "opencollective", @@ -8012,9 +8009,9 @@ "dev": true }, "node_modules/process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.1.0.tgz", + "integrity": "sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==", "dev": true }, "node_modules/progress": { @@ -8091,43 +8088,65 @@ } }, "node_modules/puppeteer": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.1.0.tgz", - "integrity": "sha512-UyJ5gz5JNjuFo6VJzIf+qDNjbSWGSoAMLuW990eErcrH6sZP85EbpLi6yG50euTMudxO/lsj4w1VNDNogHv6dA==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.3.0.tgz", + "integrity": "sha512-WJbi/ULaeuFOz7cfMgJlJCBAZiyqIFeQ6os4h5ex3PVTt2qosXgwI9eruFZqFAwJRv8x5pOuMhWR0aSRgyDqEg==", "dev": true, "hasInstallScript": true, "dependencies": { "cosmiconfig": "7.0.1", + "devtools-protocol": "0.0.1056733", "https-proxy-agent": "5.0.1", "progress": "2.0.3", "proxy-from-env": "1.1.0", - "puppeteer-core": "19.1.0" + "puppeteer-core": "19.3.0" }, "engines": { "node": ">=14.1.0" } }, "node_modules/puppeteer-core": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.1.0.tgz", - "integrity": "sha512-xIIJJuvqWbUwNzaB7l0TyChJYHdLvLhcHQiBLLKsMfvaQXnVa0Fzooq3Zb5bc01Q/b7XiP9pqDvUcYWSmzZQHA==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.3.0.tgz", + "integrity": "sha512-P8VAAOBnBJo/7DKJnj1b0K9kZBF2D8lkdL94CjJ+DZKCp182LQqYemPI9omUSZkh4bgykzXjZhaVR1qtddTTQg==", "dev": true, "dependencies": { "cross-fetch": "3.1.5", "debug": "4.3.4", - "devtools-protocol": "0.0.1045489", + "devtools-protocol": "0.0.1056733", "extract-zip": "2.0.1", "https-proxy-agent": "5.0.1", "proxy-from-env": "1.1.0", "rimraf": "3.0.2", "tar-fs": "2.1.1", "unbzip2-stream": "1.4.3", - "ws": "8.9.0" + "ws": "8.10.0" }, "engines": { "node": ">=14.1.0" } }, + "node_modules/puppeteer-core/node_modules/ws": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz", + "integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==", + "dev": true, + "engines": { + "node": ">=10.0.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, "node_modules/qs": { "version": "6.5.3", "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.3.tgz", @@ -8146,6 +8165,26 @@ "inherits": "~2.0.3" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", @@ -8330,9 +8369,9 @@ } }, "node_modules/regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "node_modules/regexpp": { @@ -8578,24 +8617,41 @@ } }, "node_modules/run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } }, "node_modules/rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", "dev": true, "dependencies": { "tslib": "^2.1.0" } }, "node_modules/rxjs/node_modules/tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true }, "node_modules/safe-buffer": { @@ -8694,6 +8750,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "optional": true + }, "node_modules/set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -8787,6 +8850,65 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "dev": true, + "optional": true, + "dependencies": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/simple-get/node_modules/decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "optional": true, + "dependencies": { + "mimic-response": "^2.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/simple-get/node_modules/mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -8825,9 +8947,9 @@ } }, "node_modules/sonic-boom": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.0.tgz", - "integrity": "sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.1.tgz", + "integrity": "sha512-iITeTHxy3B9FGu8aVdiDXUVAcHMF9Ss0cCsAOo2HfCrmVGT3/DT5oYaeu0M/YKZDlKTvChEyPq0zI9Hf33EX6A==", "dev": true, "dependencies": { "atomic-sleep": "^1.0.0" @@ -8961,9 +9083,9 @@ } }, "node_modules/stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" @@ -9167,15 +9289,15 @@ "dev": true }, "node_modules/stylelint": { - "version": "14.14.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.14.0.tgz", - "integrity": "sha512-yUI+4xXfPHVnueYddSQ/e1GuEA/2wVhWQbGj16AmWLtQJtn28lVxfS4b0CsWyVRPgd3Auzi0NXOthIEUhtQmmA==", + "version": "14.15.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.15.0.tgz", + "integrity": "sha512-JOgDAo5QRsqiOZPZO+B9rKJvBm64S0xasbuRPAbPs6/vQDgDCnZLIiw6XcAS6GQKk9k1sBWR6rmH3Mfj8OknKg==", "dev": true, "dependencies": { "@csstools/selector-specificity": "^2.0.2", "balanced-match": "^2.0.0", "colord": "^2.9.3", - "cosmiconfig": "^7.0.1", + "cosmiconfig": "^7.1.0", "css-functions-list": "^3.1.0", "debug": "^4.3.4", "fast-glob": "^3.2.12", @@ -9189,13 +9311,13 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.25.0", + "known-css-properties": "^0.26.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.17", + "postcss": "^8.4.19", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", @@ -9207,7 +9329,7 @@ "style-search": "^0.1.0", "supports-hyperlinks": "^2.3.0", "svg-tags": "^1.0.0", - "table": "^6.8.0", + "table": "^6.8.1", "v8-compile-cache": "^2.3.0", "write-file-atomic": "^4.0.2" }, @@ -9249,6 +9371,22 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, + "node_modules/stylelint/node_modules/cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "dependencies": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/stylelint/node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -9364,18 +9502,18 @@ "dev": true }, "node_modules/sweetalert2": { - "version": "11.4.33", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.33.tgz", - "integrity": "sha512-gnXH7c7xFzs9BtmHBVH4Fl/cEKhOhf4gv6UuZpFqUoFWtwIKG5sjNkcDNYW+8v3wyFestLab5qI4i1H1MkN0Pg==", + "version": "11.6.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.6.8.tgz", + "integrity": "sha512-0YHMaqF3DC67EI9uZzHpbU34rQV3acEFlnUCYmDSDNkeNOSFtSlF4DbWilfln+iUYv9s9aqbREXmKZRJqh5G5w==", "funding": { "type": "individual", - "url": "https://sweetalert2.github.io/#donations" + "url": "https://github.com/sponsors/limonte" } }, "node_modules/table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "dependencies": { "ajv": "^8.0.1", @@ -9389,9 +9527,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -9481,6 +9619,24 @@ "node": ">=8" } }, + "node_modules/tar": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "dev": true, + "optional": true, + "dependencies": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, "node_modules/tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -9509,13 +9665,23 @@ "node": ">=6" } }, - "node_modules/temp-dir": { + "node_modules/tar/node_modules/chownr": { "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", "dev": true, + "optional": true, "engines": { - "node": ">=8" + "node": ">=10" + } + }, + "node_modules/temp-dir": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", + "dev": true, + "engines": { + "node": ">=14.16" } }, "node_modules/text-table": { @@ -9796,9 +9962,9 @@ } }, "node_modules/typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -9987,14 +10153,14 @@ } }, "node_modules/web-ext": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.3.1.tgz", - "integrity": "sha512-ZTfktd1zcQpWaFAM3U+IQW674G89d1IW/Oh0Ncw9LwFvKvAcW/nA5EB4pwqB8LiW/6OSYQhHBP4x2XUTBu1SKg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.4.0.tgz", + "integrity": "sha512-dT2HJaGNXxRNuOtzaVBtEULccL0kM2SN1ark1NnN/ZSlbucobBxCDj6119iki72YyuXpaXZCJGqfZtVf1Znocg==", "dev": true, "dependencies": { - "@babel/runtime": "7.19.4", + "@babel/runtime": "7.20.1", "@devicefarmer/adbkit": "3.2.3", - "addons-linter": "5.18.0", + "addons-linter": "5.23.0", "bunyan": "1.8.15", "camelcase": "7.0.0", "chrome-launcher": "0.15.1", @@ -10005,11 +10171,11 @@ "fs-extra": "10.1.0", "fx-runner": "1.3.0", "import-fresh": "3.3.0", - "jose": "4.10.0", + "jose": "4.11.1", "mkdirp": "1.0.4", "multimatch": "6.0.0", "mz": "2.7.0", - "node-fetch": "3.2.10", + "node-fetch": "3.3.0", "node-notifier": "10.0.1", "open": "8.4.0", "parse-json": "6.0.2", @@ -10021,8 +10187,8 @@ "tmp": "0.2.1", "update-notifier": "6.0.2", "watchpack": "2.4.0", - "ws": "8.9.0", - "yargs": "17.6.0", + "ws": "8.11.0", + "yargs": "17.6.2", "zip-dir": "2.0.0" }, "bin": { @@ -10083,9 +10249,9 @@ } }, "node_modules/web-ext/node_modules/node-fetch": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", - "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", + "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", "dev": true, "dependencies": { "data-uri-to-buffer": "^4.0.0", @@ -10191,6 +10357,16 @@ "which": "bin/which" } }, + "node_modules/wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "optional": true, + "dependencies": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "node_modules/widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -10307,9 +10483,9 @@ "dev": true }, "node_modules/ws": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz", - "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true, "engines": { "node": ">=10.0.0" @@ -10386,9 +10562,9 @@ } }, "node_modules/yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "dev": true, "dependencies": { "cliui": "^8.0.1", @@ -10397,7 +10573,7 @@ "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" @@ -10413,9 +10589,9 @@ } }, "node_modules/yargs/node_modules/yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { "node": ">=12" @@ -10493,12 +10669,12 @@ } }, "@babel/runtime": { - "version": "7.19.4", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.19.4.tgz", - "integrity": "sha512-EXpLCrk55f+cYqmHsSR+yD/0gAIMxxA9QK9lnQWzhMCvt+YmoBN7Zx94s++Kv0+unHk39vxNO8t+CMA2WSS3wA==", + "version": "7.20.1", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.20.1.tgz", + "integrity": "sha512-mrzLkl6U9YLF8qpqI7TB82PESyEGjm/0Ly91jG575eVxMMlb8fYfOXFZIJ8XfLrJZQbm7dlKry2bJmXBUEkdFg==", "dev": true, "requires": { - "regenerator-runtime": "^0.13.4" + "regenerator-runtime": "^0.13.10" } }, "@csstools/selector-specificity": { @@ -10582,23 +10758,6 @@ "resolved": "https://registry.npmjs.org/@flowcrypt/fine-uploader/-/fine-uploader-5.16.4.tgz", "integrity": "sha512-m4jVj6ZNlG2LK+KPjQfJBIXnhn3TC2o/RdkBzj2a0XIyEX9DfjgqeZhUdW6d276DG4vAriLDN/4Ezz545AA7TA==" }, - "@humanwhocodes/config-array": { - "version": "0.10.5", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.10.5.tgz", - "integrity": "sha512-XVVDtp+dVvRxMoxSiSfasYaG02VEe1qH5cKgMQJWhol6HwzbcqoCMJi8dAGoYAO57jhUyhI6cWuRiTcRaDaYug==", - "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.1", - "debug": "^4.1.1", - "minimatch": "^3.0.4" - } - }, - "@humanwhocodes/gitignore-to-minimatch": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@humanwhocodes/gitignore-to-minimatch/-/gitignore-to-minimatch-1.0.2.tgz", - "integrity": "sha512-rSqmMJDdLFUsyxR6FMtD00nfQKKLFb1kv+qBbOVKqErvloEIJLo5bDTJTQNTYgeyp78JsA7u/NPi5jT1GR/MuA==", - "dev": true - }, "@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -10617,35 +10776,53 @@ "integrity": "sha512-oV4PyZfwJNtmFWhvlJLqYIX1Nn22ML8FZpS16ZUKv0hg7414xV1fjsGqxQzLT2dyK92TKxsJSwMOd7VNHAtPmA==", "dev": true }, + "@mapbox/node-pre-gyp": { + "version": "1.0.10", + "resolved": "https://registry.npmjs.org/@mapbox/node-pre-gyp/-/node-pre-gyp-1.0.10.tgz", + "integrity": "sha512-4ySo4CjzStuprMwk35H5pPbkymjv1SF3jGLj6rAHp/xT/RF7TL7bd9CTm1xDY49K2qF7jmR/g7k+SkLETP6opA==", + "dev": true, + "optional": true, + "requires": { + "detect-libc": "^2.0.0", + "https-proxy-agent": "^5.0.0", + "make-dir": "^3.1.0", + "node-fetch": "^2.6.7", + "nopt": "^5.0.0", + "npmlog": "^5.0.1", + "rimraf": "^3.0.2", + "semver": "^7.3.5", + "tar": "^6.1.11" + } + }, "@mdn/browser-compat-data": { - "version": "5.2.6", - "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.2.6.tgz", - "integrity": "sha512-KJfP6iTcVX+R5OSC4NOIF4V9fTyifcjwmdkOk7UzsaWxkF21rc6KhGlohqiSRVEynidGO1EEyyYf/PD3jsM1gA==", + "version": "5.2.17", + "resolved": "https://registry.npmjs.org/@mdn/browser-compat-data/-/browser-compat-data-5.2.17.tgz", + "integrity": "sha512-aA+rFHhXmq14GVIcEWNk8OntLEOQFwEZk9ZgG5VcDquz+pQhIjJPXacR+rwL9Z0Elfg909EcRRHC96p06/CNUg==", "dev": true }, "@nodelib/fs.scandir": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz", - "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==", + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "requires": { - "@nodelib/fs.stat": "2.0.3", + "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" } }, "@nodelib/fs.stat": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz", - "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==", + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", "dev": true }, "@nodelib/fs.walk": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz", - "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==", + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, "requires": { - "@nodelib/fs.scandir": "2.1.3", + "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" } }, @@ -10730,9 +10907,9 @@ "dev": true }, "@types/chai": { - "version": "4.3.3", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.3.tgz", - "integrity": "sha512-hC7OMnszpxhZPduX+m+nrx+uFoLkWOMiR4oa/AZF3MuSETYTZmFfJAHqZEM8MVlvfG7BEUcgvtwoCTxBp6hm3g==", + "version": "4.3.4", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.4.tgz", + "integrity": "sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==", "dev": true }, "@types/chai-as-promised": { @@ -10745,9 +10922,9 @@ } }, "@types/chrome": { - "version": "0.0.199", - "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.199.tgz", - "integrity": "sha512-BDuKiS8iZONsvTFSjbJlmHAwkYpkcWtG7Z7ESDJ/vf0QlcaXX6q4Xzi7euDNjIS4ZMA4kSODPGBHGVdC2lAHzw==", + "version": "0.0.203", + "resolved": "https://registry.npmjs.org/@types/chrome/-/chrome-0.0.203.tgz", + "integrity": "sha512-JlQNebwpBETVc8U1Rr2inDFuOTtn0lahRAhnddx1dd0S5RrLAFJEEsyIu7AXI14mkCgSunksnuLGioH8kvBqRA==", "dev": true, "requires": { "@types/filesystem": "*", @@ -10755,9 +10932,9 @@ } }, "@types/dompurify": { - "version": "2.3.4", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.3.4.tgz", - "integrity": "sha512-EXzDatIb5EspL2eb/xPGmaC8pePcTHrkDCONjeisusLFrVfl38Pjea/R0YJGu3k9ZQadSvMqW0WXPI2hEo2Ajg==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-2.4.0.tgz", + "integrity": "sha512-IDBwO5IZhrKvHFUl+clZxgf3hn2b/lU6H1KaBShPkQyGJUQ0xwebezIPSuiyGwfz1UzJWQl4M7BDxtHtCCPlTg==", "dev": true, "requires": { "@types/trusted-types": "*" @@ -10891,9 +11068,9 @@ } }, "@types/semver": { - "version": "7.3.12", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.12.tgz", - "integrity": "sha512-WwA1MW0++RfXmCr12xeYOOC5baSC9mSb0ZqCquFzKhcoF4TvHu5MKOuXsncgZcpVFhB1pXd5hZmM0ryAoCp12A==", + "version": "7.3.13", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.3.13.tgz", + "integrity": "sha512-21cFJr9z3g5dW8B0CVI9g2O9beqaThGQ6ZFBqHfwhzLDKUxaqTIy3vnfah/UPkfOiF2pLq+tGz+W8RyCskuslw==", "dev": true }, "@types/sizzle": { @@ -10924,69 +11101,70 @@ } }, "@typescript-eslint/eslint-plugin": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.40.1.tgz", - "integrity": "sha512-FsWboKkWdytGiXT5O1/R9j37YgcjO8MKHSUmWnIEjVaz0krHkplPnYi7mwdb+5+cs0toFNQb0HIrN7zONdIEWg==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.45.0.tgz", + "integrity": "sha512-CXXHNlf0oL+Yg021cxgOdMHNTXD17rHkq7iW6RFHoybdFgQBjU3yIXhhcPpGwr1CjZlo6ET8C6tzX5juQoXeGA==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/type-utils": "5.40.1", - "@typescript-eslint/utils": "5.40.1", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/type-utils": "5.45.0", + "@typescript-eslint/utils": "5.45.0", "debug": "^4.3.4", "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", "regexpp": "^3.2.0", "semver": "^7.3.7", "tsutils": "^3.21.0" } }, "@typescript-eslint/parser": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.40.1.tgz", - "integrity": "sha512-IK6x55va5w4YvXd4b3VrXQPldV9vQTxi5ov+g4pMANsXPTXOcfjx08CRR1Dfrcc51syPtXHF5bgLlMHYFrvQtg==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.45.0.tgz", + "integrity": "sha512-brvs/WSM4fKUmF5Ot/gEve6qYiCMjm6w4HkHPfS6ZNmxTS0m0iNN4yOChImaCkqc1hRwFGqUyanMXuGal6oyyQ==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/typescript-estree": "5.40.1", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", "debug": "^4.3.4" } }, "@typescript-eslint/scope-manager": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.40.1.tgz", - "integrity": "sha512-jkn4xsJiUQucI16OLCXrLRXDZ3afKhOIqXs4R3O+M00hdQLKR58WuyXPZZjhKLFCEP2g+TXdBRtLQ33UfAdRUg==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.45.0.tgz", + "integrity": "sha512-noDMjr87Arp/PuVrtvN3dXiJstQR1+XlQ4R1EvzG+NMgXi8CuMCXpb8JqNtFHKceVSQ985BZhfRdowJzbv4yKw==", "dev": true, "requires": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1" + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0" } }, "@typescript-eslint/type-utils": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.40.1.tgz", - "integrity": "sha512-DLAs+AHQOe6n5LRraXiv27IYPhleF0ldEmx6yBqBgBLaNRKTkffhV1RPsjoJBhVup2zHxfaRtan8/YRBgYhU9Q==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.45.0.tgz", + "integrity": "sha512-DY7BXVFSIGRGFZ574hTEyLPRiQIvI/9oGcN8t1A7f6zIs6ftbrU0nhyV26ZW//6f85avkwrLag424n+fkuoJ1Q==", "dev": true, "requires": { - "@typescript-eslint/typescript-estree": "5.40.1", - "@typescript-eslint/utils": "5.40.1", + "@typescript-eslint/typescript-estree": "5.45.0", + "@typescript-eslint/utils": "5.45.0", "debug": "^4.3.4", "tsutils": "^3.21.0" } }, "@typescript-eslint/types": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.40.1.tgz", - "integrity": "sha512-Icg9kiuVJSwdzSQvtdGspOlWNjVDnF3qVIKXdJ103o36yRprdl3Ge5cABQx+csx960nuMF21v8qvO31v9t3OHw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.45.0.tgz", + "integrity": "sha512-QQij+u/vgskA66azc9dCmx+rev79PzX8uDHpsqSjEFtfF2gBUTRCpvYMh2gw2ghkJabNkPlSUCimsyBEQZd1DA==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.40.1.tgz", - "integrity": "sha512-5QTP/nW5+60jBcEPfXy/EZL01qrl9GZtbgDZtDPlfW5zj/zjNrdI2B5zMUHmOsfvOr2cWqwVdWjobCiHcedmQA==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.45.0.tgz", + "integrity": "sha512-maRhLGSzqUpFcZgXxg1qc/+H0bT36lHK4APhp0AEUVrpSwXiRAomm/JGjSG+kNUio5kAa3uekCYu/47cnGn5EQ==", "dev": true, "requires": { - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/visitor-keys": "5.40.1", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/visitor-keys": "5.45.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", @@ -10995,28 +11173,28 @@ } }, "@typescript-eslint/utils": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.40.1.tgz", - "integrity": "sha512-a2TAVScoX9fjryNrW6BZRnreDUszxqm9eQ9Esv8n5nXApMW0zeANUYlwh/DED04SC/ifuBvXgZpIK5xeJHQ3aw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.45.0.tgz", + "integrity": "sha512-OUg2JvsVI1oIee/SwiejTot2OxwU8a7UfTFMOdlhD2y+Hl6memUSL4s98bpUTo8EpVEr0lmwlU7JSu/p2QpSvA==", "dev": true, "requires": { "@types/json-schema": "^7.0.9", "@types/semver": "^7.3.12", - "@typescript-eslint/scope-manager": "5.40.1", - "@typescript-eslint/types": "5.40.1", - "@typescript-eslint/typescript-estree": "5.40.1", + "@typescript-eslint/scope-manager": "5.45.0", + "@typescript-eslint/types": "5.45.0", + "@typescript-eslint/typescript-estree": "5.45.0", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0", "semver": "^7.3.7" } }, "@typescript-eslint/visitor-keys": { - "version": "5.40.1", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.40.1.tgz", - "integrity": "sha512-A2DGmeZ+FMja0geX5rww+DpvILpwo1OsiQs0M+joPWJYsiEFBLsH0y1oFymPNul6Z5okSmHpP4ivkc2N0Cgfkw==", + "version": "5.45.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.45.0.tgz", + "integrity": "sha512-jc6Eccbn2RtQPr1s7th6jJWQHBHI6GBVQkCHoJFQ5UreaKm59Vxw+ynQUPPY2u2Amquc+7tmEoC2G52ApsGNNg==", "dev": true, "requires": { - "@typescript-eslint/types": "5.40.1", + "@typescript-eslint/types": "5.45.0", "eslint-visitor-keys": "^3.3.0" }, "dependencies": { @@ -11028,6 +11206,13 @@ } } }, + "abbrev": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", + "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", + "dev": true, + "optional": true + }, "abort-controller": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/abort-controller/-/abort-controller-3.0.0.tgz", @@ -11038,9 +11223,9 @@ } }, "acorn": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.0.tgz", - "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==", + "version": "8.8.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.1.tgz", + "integrity": "sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==", "dev": true }, "acorn-jsx": { @@ -11057,25 +11242,25 @@ "dev": true }, "addons-linter": { - "version": "5.18.0", - "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-5.18.0.tgz", - "integrity": "sha512-pce7nSuf/fNesDTmiD077auB15gcWZVHSVFmmAU/mm4BpzDPJBYp5rBYVMDaLjTAsYxR6Qq1ICBN8zryso2UxQ==", + "version": "5.23.0", + "resolved": "https://registry.npmjs.org/addons-linter/-/addons-linter-5.23.0.tgz", + "integrity": "sha512-Vo6+5YlM2Ge3yYMY+gNg9Smcfcl1J0ZMfGVXnGJjUwDVHuszHVIvurunQuJURnO4FR1gi4Vy1sWye8ArRL5LOw==", "dev": true, "requires": { - "@mdn/browser-compat-data": "5.2.6", - "addons-moz-compare": "1.2.0", + "@mdn/browser-compat-data": "5.2.17", + "addons-moz-compare": "1.3.0", "addons-scanner-utils": "8.1.0", - "ajv": "8.11.0", + "ajv": "8.11.2", "ajv-merge-patch": "5.0.1", "chalk": "4.1.2", "cheerio": "1.0.0-rc.12", "columnify": "1.6.0", "common-tags": "1.8.2", "deepmerge": "4.2.2", - "eslint": "8.25.0", + "eslint": "8.28.0", "eslint-plugin-no-unsanitized": "4.0.1", "eslint-visitor-keys": "3.3.0", - "espree": "9.4.0", + "espree": "9.4.1", "esprima": "4.0.1", "fluent-syntax": "0.13.0", "glob": "8.0.3", @@ -11083,22 +11268,22 @@ "is-mergeable-object": "1.1.1", "jed": "1.1.1", "os-locale": "5.0.0", - "pino": "8.6.1", - "postcss": "8.4.18", + "pino": "8.7.0", + "postcss": "8.4.19", "relaxed-json": "1.0.3", "semver": "7.3.8", "sha.js": "2.4.11", "source-map-support": "0.5.21", "tosource": "1.0.0", "upath": "2.0.1", - "yargs": "17.6.0", + "yargs": "17.6.2", "yauzl": "2.10.0" }, "dependencies": { "ajv": { - "version": "8.11.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.0.tgz", - "integrity": "sha512-wGgprdCvMalC0BztXvitD2hC04YffAvtsUn93JbGXYLAtCUO4xd17mCCZQxUOItiBwZvJScWo8NIvQMQ71rdpg==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -11126,12 +11311,6 @@ "color-convert": "^2.0.1" } }, - "argparse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", - "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true - }, "brace-expansion": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", @@ -11166,206 +11345,46 @@ "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, - "escape-string-regexp": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", - "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true - }, - "eslint": { - "version": "8.25.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.25.0.tgz", - "integrity": "sha512-DVlJOZ4Pn50zcKW5bYH7GQK/9MsoQG2d5eDH0ebEkE8PbgzTTmtt/VTH9GGJ4BfeZCpBLqFfvsjX35UacUL83A==", - "dev": true, - "requires": { - "@eslint/eslintrc": "^1.3.3", - "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/module-importer": "^1.0.1", - "ajv": "^6.10.0", - "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", - "debug": "^4.3.2", - "doctrine": "^3.0.0", - "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.1.1", - "eslint-utils": "^3.0.0", - "eslint-visitor-keys": "^3.3.0", - "espree": "^9.4.0", - "esquery": "^1.4.0", - "esutils": "^2.0.2", - "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", - "find-up": "^5.0.0", - "glob-parent": "^6.0.1", - "globals": "^13.15.0", - "globby": "^11.1.0", - "grapheme-splitter": "^1.0.4", - "ignore": "^5.2.0", - "import-fresh": "^3.0.0", - "imurmurhash": "^0.1.4", - "is-glob": "^4.0.0", - "js-sdsl": "^4.1.4", - "js-yaml": "^4.1.0", - "json-stable-stringify-without-jsonify": "^1.0.1", - "levn": "^0.4.1", - "lodash.merge": "^4.6.2", - "minimatch": "^3.1.2", - "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "regexpp": "^3.2.0", - "strip-ansi": "^6.0.1", - "strip-json-comments": "^3.1.0", - "text-table": "^0.2.0" - }, - "dependencies": { - "ajv": { - "version": "6.12.6", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", - "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", - "dev": true, - "requires": { - "fast-deep-equal": "^3.1.1", - "fast-json-stable-stringify": "^2.0.0", - "json-schema-traverse": "^0.4.1", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", - "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true - } - } - }, - "eslint-scope": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.1.1.tgz", - "integrity": "sha512-QKQM/UXpIiHcLqJ5AOyIW7XZmzjkzQXYE54n1++wb0u9V/abW3l9uQnxX8Z5Xd18xyKIMTUAyQ0k1e8pz6LUrw==", - "dev": true, - "requires": { - "esrecurse": "^4.3.0", - "estraverse": "^5.2.0" - } - }, "eslint-visitor-keys": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.3.0.tgz", "integrity": "sha512-mQ+suqKJVyeuwGYHAdjMFqjCyfl8+Ldnxuyp3ldiMBFKkvytrXUZWaiPCEav8qDHKty44bD+qV1IP4T+w+xXRA==", "dev": true }, - "estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true - }, - "find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", - "dev": true, - "requires": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" - } - }, "glob": { "version": "8.0.3", "resolved": "https://registry.npmjs.org/glob/-/glob-8.0.3.tgz", "integrity": "sha512-ull455NHSHI/Y1FqGaaYFaLGkNMMJbavMrEGFXG/PGrg6y7sutWHUHrz6gy6WEBH6akM1M414dWKCNs+IhKdiQ==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" - }, - "dependencies": { - "minimatch": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.0.tgz", - "integrity": "sha512-9TPBGGak4nHfGZsPBohm9AWg6NoT7QTCehS3BIJABslyZbzxfV78QM2Y6+i741OPZIafFAaiiEMh5OyIrJPgtg==", - "dev": true, - "requires": { - "brace-expansion": "^2.0.1" - } - } - } - }, - "glob-parent": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", - "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", - "dev": true, - "requires": { - "is-glob": "^4.0.3" - } - }, - "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - }, - "locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", - "dev": true, - "requires": { - "p-locate": "^5.0.0" - } - }, - "p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", - "dev": true, - "requires": { - "yocto-queue": "^0.1.0" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^5.0.1", + "once": "^1.3.0" } }, - "p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", - "dev": true, - "requires": { - "p-limit": "^3.0.2" - } + "json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true }, - "strip-ansi": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", - "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "minimatch": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.1.tgz", + "integrity": "sha512-362NP+zlprccbEt/SkxKfRMHnNY85V74mVnpUpNyr3F35covl09Kec7/sEFLt3RA4oXmewtoaanoIf67SE5Y5g==", "dev": true, "requires": { - "ansi-regex": "^5.0.1" + "brace-expansion": "^2.0.1" } - }, - "yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true } } }, "addons-moz-compare": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.2.0.tgz", - "integrity": "sha512-COG8qk2/dubPqabfcoJW4E7pm2EQDI43iMrHnhlobvq/uRMEzx/PYJ1KaUZ97Vgg44R3QdRG5CvDsTRbMUHcDw==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/addons-moz-compare/-/addons-moz-compare-1.3.0.tgz", + "integrity": "sha512-/rXpQeaY0nOKhNx00pmZXdk5Mu+KhVlL3/pSBuAYwrxRrNiTvI/9xfQI8Lmm7DMMl+PDhtfAHY/0ibTpdeoQQQ==", "dev": true }, "addons-scanner-utils": { @@ -11405,14 +11424,6 @@ "requires": { "clean-stack": "^2.0.0", "indent-string": "^4.0.0" - }, - "dependencies": { - "indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true - } } }, "ajv": { @@ -11484,6 +11495,24 @@ "picomatch": "^2.0.4" } }, + "aproba": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/aproba/-/aproba-2.0.0.tgz", + "integrity": "sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==", + "dev": true, + "optional": true + }, + "are-we-there-yet": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/are-we-there-yet/-/are-we-there-yet-2.0.0.tgz", + "integrity": "sha512-Ci/qENmwHnsYo9xKIcUJN5LeDKdJ6R1Z1j9V/J5wyq8nh/mYPEpIKJbBZXtZjG04HiK7zV/p6Vs9952MrMeUIw==", + "dev": true, + "optional": true, + "requires": { + "delegates": "^1.0.0", + "readable-stream": "^3.6.0" + } + }, "argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", @@ -11581,22 +11610,22 @@ "dev": true }, "ava": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/ava/-/ava-5.0.1.tgz", - "integrity": "sha512-nS1eK3HhWaC+eGHtteF5j4yZMjaIE+q2o+oyqD75xsmS87R5sGlxADYWkFIGyB28jrDmAATZAAx+s3JhYsnhNw==", + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/ava/-/ava-5.1.0.tgz", + "integrity": "sha512-e5VFrSQ0WBPyZJWRXVrO7RFOizFeNM0t2PORwrPvWtApgkORI6cvGnY3GX1G+lzpd0HjqNx5Jus22AhxVnUMNA==", "dev": true, "requires": { - "acorn": "^8.8.0", + "acorn": "^8.8.1", "acorn-walk": "^8.2.0", - "ansi-styles": "^6.1.1", + "ansi-styles": "^6.2.1", "arrgv": "^1.0.2", "arrify": "^3.0.0", "callsites": "^4.0.0", "cbor": "^8.1.0", - "chalk": "^5.0.1", + "chalk": "^5.1.2", "chokidar": "^3.5.3", "chunkd": "^2.0.1", - "ci-info": "^3.4.0", + "ci-info": "^3.6.1", "ci-parallel-vars": "^1.0.1", "clean-yaml-object": "^0.1.0", "cli-truncate": "^3.1.0", @@ -11606,7 +11635,7 @@ "currently-unhandled": "^0.4.1", "debug": "^4.3.4", "del": "^7.0.0", - "emittery": "^1.0.0", + "emittery": "^1.0.1", "figures": "^5.0.0", "globby": "^13.1.2", "ignore-by-default": "^2.1.0", @@ -11625,12 +11654,12 @@ "pretty-ms": "^8.0.0", "resolve-cwd": "^3.0.0", "slash": "^3.0.0", - "stack-utils": "^2.0.5", + "stack-utils": "^2.0.6", "strip-ansi": "^7.0.1", "supertap": "^3.0.1", - "temp-dir": "^2.0.0", - "write-file-atomic": "^4.0.2", - "yargs": "^17.5.1" + "temp-dir": "^3.0.0", + "write-file-atomic": "^5.0.0", + "yargs": "^17.6.2" }, "dependencies": { "ansi-styles": { @@ -11652,9 +11681,9 @@ "dev": true }, "chalk": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.0.1.tgz", - "integrity": "sha512-Fo07WOYGqMfCWHOzSXOt2CxDbC6skS/jO9ynEcmpANMoPrD+W1r1K6Vx7iNm+AQmETU1Xr2t+n8nzkV9t6xh3w==", + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.1.2.tgz", + "integrity": "sha512-E5CkT4jWURs1Vy5qGJye+XwCkNj7Od3Af7CP6SujMetSMkLs8Do2RWJK5yx1wamHV/op8Rz+9rltjaTQWDnEFQ==", "dev": true }, "globby": { @@ -11707,9 +11736,9 @@ "dev": true }, "write-file-atomic": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", - "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-5.0.0.tgz", + "integrity": "sha512-R7NYMnHSlV42K54lwY9lvW6MnSm1HSJqZL3xiSgi9E7//FYaI74r2G0rd+/X6VAMkHEdzxQaU5HUOXWUz5kA/w==", "dev": true, "requires": { "imurmurhash": "^0.1.4", @@ -11995,6 +12024,18 @@ "quick-lru": "^4.0.1" } }, + "canvas": { + "version": "2.10.2", + "resolved": "https://registry.npmjs.org/canvas/-/canvas-2.10.2.tgz", + "integrity": "sha512-FSmlsip0nZ0U4Zcfht0qBJqDhlfGuevTZKE8h+dBOYrJjGvY3iqMGSzzbvkaFhvMXiVxfcMaPHS/kge++T5SKg==", + "dev": true, + "optional": true, + "requires": { + "@mapbox/node-pre-gyp": "^1.0.0", + "nan": "^2.17.0", + "simple-get": "^3.0.3" + } + }, "caseless": { "version": "0.12.0", "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", @@ -12011,14 +12052,14 @@ } }, "chai": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.6.tgz", - "integrity": "sha512-bbcp3YfHCUzMOvKqsztczerVgBKSsEijCySNlHHbX3VG1nskvqjz5Rfso1gGwD6w6oOV3eI60pKuMOV5MV7p3Q==", + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/chai/-/chai-4.3.7.tgz", + "integrity": "sha512-HLnAzZ2iupm25PlN0xFreAlBA5zaBSv3og0DdeGA4Ar6h6rJ3A0rolRUKJhSF2V10GZKDgWF/VmAEsNWjCRB+A==", "dev": true, "requires": { "assertion-error": "^1.1.0", "check-error": "^1.0.2", - "deep-eql": "^3.0.1", + "deep-eql": "^4.1.2", "get-func-name": "^2.0.0", "loupe": "^2.3.1", "pathval": "^1.1.1", @@ -12206,9 +12247,9 @@ "dev": true }, "ci-info": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.5.0.tgz", - "integrity": "sha512-yH4RezKOGlOhxkmhbeNuC4eYZKAUsEaGtBuBzDDP1eFUKiccDWzBABxBfOx31IDwDIXMTxWuwAxUGModvkbuVw==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.6.2.tgz", + "integrity": "sha512-lVZdhvbEudris15CLytp2u6Y0p5EKfztae9Fqa189MfNmln9F33XuH69v5fvNfiRN5/0eAUz2yJL3mo+nhaRKg==", "dev": true }, "ci-parallel-vars": { @@ -12338,6 +12379,13 @@ "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", "dev": true }, + "color-support": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/color-support/-/color-support-1.1.3.tgz", + "integrity": "sha512-qiBjkpbMLO/HL68y+lh4q0/O1MZFj2RX6X/KmMa3+gJD3z+WwI1ZzDHysvqHGS3mP6mznPckpXmw1nI9cJjyRg==", + "dev": true, + "optional": true + }, "colord": { "version": "2.9.3", "resolved": "https://registry.npmjs.org/colord/-/colord-2.9.3.tgz", @@ -12496,6 +12544,13 @@ } } }, + "console-control-strings": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz", + "integrity": "sha512-ty/fTekppD2fIwRvnZAVdeOiGd1c7YXEixbgJTNzqcxJWKQnjJ/V1bNEEE6hygpM3WjwHFUVK6HTjWSzV4a8sQ==", + "dev": true, + "optional": true + }, "convert-to-spaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/convert-to-spaces/-/convert-to-spaces-2.0.1.tgz", @@ -12729,9 +12784,9 @@ } }, "deep-eql": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-3.0.1.tgz", - "integrity": "sha512-+QeIQyN5ZuO+3Uk5DYh6/1eKO0m0YmJFGNmFHGACpf1ClL1nmlV/p4gNgbl2pJGxgXb4faqo6UE+M5ACEMyVcw==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/deep-eql/-/deep-eql-4.1.2.tgz", + "integrity": "sha512-gT18+YW4CcW/DBNTwAmqTtkJh7f9qqScu2qFVlx7kCoeY9tlBu9cUcr7+I+Z/noG8INehS3xQgLpTtd/QUTn4w==", "dev": true, "requires": { "type-detect": "^4.0.0" @@ -12833,10 +12888,24 @@ "resolved": "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz", "integrity": "sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==" }, + "delegates": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "optional": true + }, + "detect-libc": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", + "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "dev": true, + "optional": true + }, "devtools-protocol": { - "version": "0.0.1045489", - "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1045489.tgz", - "integrity": "sha512-D+PTmWulkuQW4D1NTiCRCFxF7pQPn0hgp4YyX4wAQ6xYXKOadSWPR3ENGDQ47MW/Ewc9v2rpC/UEEGahgBYpSQ==", + "version": "0.0.1056733", + "resolved": "https://registry.npmjs.org/devtools-protocol/-/devtools-protocol-0.0.1056733.tgz", + "integrity": "sha512-CmTu6SQx2g3TbZzDCAV58+LTxVdKplS7xip0g5oDXpZ+isr0rv5dDP8ToyVRywzPHkCCPKgKgScEcwz4uPWDIA==", "dev": true }, "diff": { @@ -12902,16 +12971,10 @@ "domelementtype": "^2.2.0" } }, - "dommatrix": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/dommatrix/-/dommatrix-1.0.3.tgz", - "integrity": "sha512-l32Xp/TLgWb8ReqbVJAFIvXmY7go4nTxxlWiAFyhoQw9RKEOHBZNnyGvJWqDVSPmq3Y9HlM4npqF/T6VMOXhww==", - "dev": true - }, "dompurify": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.0.tgz", - "integrity": "sha512-Be9tbQMZds4a3C6xTmz68NlMfeONA//4dOavl/1rNw50E+/QO0KVpbcU0PcaW0nsQxurXls9ZocqFxk8R2mWEA==" + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-2.4.1.tgz", + "integrity": "sha512-ewwFzHzrrneRjxzmK6oVz/rZn9VWspGFRDb4/rRtIsM1n36t9AKma/ye8syCpcw+XJ25kOK/hOG7t1j2I2yBqA==" }, "domutils": { "version": "2.8.0", @@ -12969,9 +13032,9 @@ } }, "emittery": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.0.tgz", - "integrity": "sha512-TD/u5aAn5W2HI2OukSIReNYXf/cH7U0QZHPxM4aIVYy0CmtrLCvf+7E8MuV2BbO02l6wq4sAKuFA8H16l6AHMA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/emittery/-/emittery-1.0.1.tgz", + "integrity": "sha512-2ID6FdrMD9KDLldGesP6317G78K7km/kMcwItRtVFva7I/cSEOIaLpewaUb+YLXVwdAp3Ctfxh/V5zIl1sj7dQ==", "dev": true }, "emoji-regex": { @@ -13040,15 +13103,15 @@ "dev": true }, "eslint": { - "version": "8.24.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.24.0.tgz", - "integrity": "sha512-dWFaPhGhTAiPcCgm3f6LI2MBWbogMnTJzFBbhXVRQDJPkr9pGZvVjlVfXd+vyDcWPA2Ic9L2AXPIQM0+vk/cSQ==", + "version": "8.28.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.28.0.tgz", + "integrity": "sha512-S27Di+EVyMxcHiwDrFzk8dJYAaD+/5SoWKxL1ri/71CRHsnJnRDPNt2Kzj24+MT9FDupf4aqqyqPrvI8MvQ4VQ==", "dev": true, "requires": { - "@eslint/eslintrc": "^1.3.2", - "@humanwhocodes/config-array": "^0.10.5", - "@humanwhocodes/gitignore-to-minimatch": "^1.0.2", + "@eslint/eslintrc": "^1.3.3", + "@humanwhocodes/config-array": "^0.11.6", "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", @@ -13064,14 +13127,14 @@ "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", - "glob-parent": "^6.0.1", + "glob-parent": "^6.0.2", "globals": "^13.15.0", - "globby": "^11.1.0", "grapheme-splitter": "^1.0.4", "ignore": "^5.2.0", "import-fresh": "^3.0.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", + "is-path-inside": "^3.0.3", "js-sdsl": "^4.1.4", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", @@ -13086,6 +13149,17 @@ "text-table": "^0.2.0" }, "dependencies": { + "@humanwhocodes/config-array": { + "version": "0.11.7", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.7.tgz", + "integrity": "sha512-kBbPWzN8oVMLb0hOUYXhmxggL/1cJE6ydvjDIGi9EnAGUyA7cLVKQg+d/Dsm+KZwx2czGHrCmMVLiyg8s5JPKw==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.1", + "debug": "^4.1.1", + "minimatch": "^3.0.5" + } + }, "ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", @@ -13173,6 +13247,12 @@ "is-glob": "^4.0.3" } }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -13272,9 +13352,9 @@ "dev": true }, "espree": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.0.tgz", - "integrity": "sha512-DQmnRpLj7f6TgN/NYb0MTzJXL+vJF9h3pHy4JhCIs3zwcgez8xmGg3sXHcEO97BrmO2OSvCwMdfdlyl+E9KjOw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.4.1.tgz", + "integrity": "sha512-XwctdmTO6SIvCzd9810yyNzIrOrqNYV9Koizx4C/mRhf9uq0o4yHoCEU/670pOxOL/MSraektvSAji79kX90Vg==", "dev": true, "requires": { "acorn": "^8.8.0", @@ -13673,6 +13753,16 @@ } } }, + "fs-minipass": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", + "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^3.0.0" + } + }, "fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -13726,6 +13816,36 @@ } } }, + "gauge": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/gauge/-/gauge-3.0.2.tgz", + "integrity": "sha512-+5J6MS/5XksCuXq++uFRsnUd7Ovu1XenbeuIuNRJxYWjgQbPuFhT14lAvsWfqfAmnwluf1OwMjz39HjfLPci0Q==", + "dev": true, + "optional": true, + "requires": { + "aproba": "^1.0.3 || ^2.0.0", + "color-support": "^1.1.2", + "console-control-strings": "^1.0.0", + "has-unicode": "^2.0.1", + "object-assign": "^4.1.1", + "signal-exit": "^3.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "wide-align": "^1.1.2" + }, + "dependencies": { + "strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "optional": true, + "requires": { + "ansi-regex": "^5.0.1" + } + } + } + }, "gaxios": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/gaxios/-/gaxios-5.0.0.tgz", @@ -13957,9 +14077,9 @@ } }, "googleapis": { - "version": "108.0.0", - "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-108.0.0.tgz", - "integrity": "sha512-wQuBzCObtjpfg3CksOfUlX3yT8clw/vJFdGSfs9cpn84WSxNK3U5sxYxEH3mPM+d+SrA8znKM9G8sOuwQceGIA==", + "version": "109.0.1", + "resolved": "https://registry.npmjs.org/googleapis/-/googleapis-109.0.1.tgz", + "integrity": "sha512-x286OtNu0ngzxfGz2XgRs4aMhrwutRCkCE12dh2M1jIZOpOndB7ELFXEhmtxaJ7z3257flKIbiiCJZeBO+ze/Q==", "dev": true, "requires": { "google-auth-library": "^8.0.2", @@ -14147,6 +14267,13 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, + "has-unicode": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz", + "integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ==", + "dev": true, + "optional": true + }, "has-yarn": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-yarn/-/has-yarn-3.0.0.tgz", @@ -14301,9 +14428,9 @@ "dev": true }, "husky": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.1.tgz", - "integrity": "sha512-xs7/chUH/CKdOCs7Zy0Aev9e/dKOMZf3K1Az1nar3tzlv0jfqnYtu235bstsWTmXOR0EfINrPa97yy4Lz6RiKw==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.2.tgz", + "integrity": "sha512-Tkv80jtvbnkK3mYWxPZePGFpQ/tT3HNSs/sasF9P2YfkMezDl3ON37YN6jUUI4eTg5LcyVynlb6r4eyvOmspvg==", "dev": true }, "iconv-lite": { @@ -14640,9 +14767,9 @@ "dev": true }, "jose": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/jose/-/jose-4.10.0.tgz", - "integrity": "sha512-KEhB/eLGLomWGPTb+/RNbYsTjIyx03JmbqAyIyiXBuNSa7CmNrJd5ysFhblayzs/e/vbOPMUaLnjHUMhGp4yLw==", + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/jose/-/jose-4.11.1.tgz", + "integrity": "sha512-YRv4Tk/Wlug8qicwqFNFVEZSdbROCHRAC6qu/i0dyNKr5JQdoa2pIGoS04lLO/jXQX7Z9omoNewYIVIxqZBd9Q==", "dev": true }, "jquery": { @@ -14861,9 +14988,9 @@ "dev": true }, "known-css-properties": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.25.0.tgz", - "integrity": "sha512-b0/9J1O9Jcyik1GC6KC42hJ41jKwdO/Mq8Mdo5sYN+IuRTXs2YFHZC3kZSx6ueusqa95x3wLYe/ytKjbAfGixA==", + "version": "0.26.0", + "resolved": "https://registry.npmjs.org/known-css-properties/-/known-css-properties-0.26.0.tgz", + "integrity": "sha512-5FZRzrZzNTBruuurWpvZnvP9pum+fe0HcK8z/ooo+U+Hmp4vtbyp1/QDsqmufirXy4egGzbaH/y2uCZf+6W5Kg==", "dev": true }, "latest-version": { @@ -14955,9 +15082,9 @@ } }, "lilconfig": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.5.tgz", - "integrity": "sha512-xaYmXZtTHPAw5m+xLN8ab9C+3a8YmV3asNSPOATITbtwrfbwaLJj8h66H1WMIpALCkqsIzK3h7oQ+PdX+LQ9Eg==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.0.6.tgz", + "integrity": "sha512-9JROoBW7pobfsx+Sq2JsASvCo6Pfo6WWoUW79HuB1BCoBXD4PLWJPqDF6fNj67pqBYTbAHkE57M1kS/+L1neOg==", "dev": true }, "lines-and-columns": { @@ -14976,30 +15103,30 @@ } }, "lint-staged": { - "version": "13.0.3", - "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.3.tgz", - "integrity": "sha512-9hmrwSCFroTSYLjflGI8Uk+GWAwMB4OlpU4bMJEAT5d/llQwtYKoim4bLOyLCuWFAhWEupE0vkIFqtw/WIsPug==", + "version": "13.0.4", + "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.0.4.tgz", + "integrity": "sha512-HxlHCXoYRsq9QCby5wFozmZW00hMs/9e3l+/dz6Qr8Kle4UH0kJTdABAbqhzG+3pcG6QjL9kz7NgGBfph+a5dw==", "dev": true, "requires": { "cli-truncate": "^3.1.0", - "colorette": "^2.0.17", - "commander": "^9.3.0", + "colorette": "^2.0.19", + "commander": "^9.4.1", "debug": "^4.3.4", "execa": "^6.1.0", - "lilconfig": "2.0.5", - "listr2": "^4.0.5", + "lilconfig": "2.0.6", + "listr2": "^5.0.5", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "object-inspect": "^1.12.2", "pidtree": "^0.6.0", "string-argv": "^0.3.1", - "yaml": "^2.1.1" + "yaml": "^2.1.3" }, "dependencies": { "commander": { - "version": "9.3.0", - "resolved": "https://registry.npmjs.org/commander/-/commander-9.3.0.tgz", - "integrity": "sha512-hv95iU5uXPbK83mjrJKuZyFM/LBAoCV/XhVGkS5Je6tl7sxr6A0ITMw5WoRV46/UaJ46Nllm3Xt7IaJhXTIkzw==", + "version": "9.4.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-9.4.1.tgz", + "integrity": "sha512-5EEkTNyHNGFPD2H+c/dXXfQZYa/scCKasxWcXJaWnNJ99pnQN9Vnmqow+p+PlFPE63Q6mThaZws1T+HxfpgtPw==", "dev": true }, "execa": { @@ -15074,25 +15201,25 @@ "dev": true }, "yaml": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.1.tgz", - "integrity": "sha512-o96x3OPo8GjWeSLF+wOAbrPfhFOGY0W00GNaxCDv+9hkcDJEnev1yh8S7pgHF0ik6zc8sQLuL8hjHjJULZp8bw==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.1.3.tgz", + "integrity": "sha512-AacA8nRULjKMX2DvWvOAdBZMOfQlypSFkjcOcu9FalllIDJ1kvlREzcdIZmidQUqqeMv7jorHjq2HlLv/+c2lg==", "dev": true } } }, "listr2": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/listr2/-/listr2-4.0.5.tgz", - "integrity": "sha512-juGHV1doQdpNT3GSTs9IUN43QJb7KHdF9uqg7Vufs/tG9VTzpFphqF4pm/ICdAABGQxsyNn9CiYA3StkI6jpwA==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.6.tgz", + "integrity": "sha512-u60KxKBy1BR2uLJNTWNptzWQ1ob/gjMzIJPZffAENzpZqbMZ/5PrXXOomDcevIS/+IB7s1mmCEtSlT2qHWMqag==", "dev": true, "requires": { "cli-truncate": "^2.1.0", - "colorette": "^2.0.16", + "colorette": "^2.0.19", "log-update": "^4.0.0", "p-map": "^4.0.0", "rfdc": "^1.3.0", - "rxjs": "^7.5.5", + "rxjs": "^7.5.7", "through": "^2.3.8", "wrap-ansi": "^7.0.0" }, @@ -15231,7 +15358,7 @@ "lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "log-update": { @@ -15361,6 +15488,25 @@ "libqp": "1.1.0" } }, + "make-dir": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz", + "integrity": "sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==", + "dev": true, + "optional": true, + "requires": { + "semver": "^6.0.0" + }, + "dependencies": { + "semver": { + "version": "6.3.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.0.tgz", + "integrity": "sha512-b39TBaTSfV6yBrapU89p5fKekE2m/NwnDocOVruQFS1/veMgdzuPcnOM34M6CwxW8jH/lxEa5rBoDeUwu5HHTw==", + "dev": true, + "optional": true + } + } + }, "make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", @@ -15546,6 +15692,27 @@ "kind-of": "^6.0.3" } }, + "minipass": { + "version": "3.3.6", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", + "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "dev": true, + "optional": true, + "requires": { + "yallist": "^4.0.0" + } + }, + "minizlib": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", + "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", + "dev": true, + "optional": true, + "requires": { + "minipass": "^3.0.0", + "yallist": "^4.0.0" + } + }, "mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -15657,9 +15824,9 @@ } }, "nan": { - "version": "2.15.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.15.0.tgz", - "integrity": "sha512-8ZtvEnA2c5aYCZYd1cvgdnU6cqwixRoYg70xPLWUws5ORTa/lnw+u4amixRS/Ac5U5mQVgp9pnlSUnbNWFaWZQ==", + "version": "2.17.0", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.17.0.tgz", + "integrity": "sha512-2ZTgtl0nJsO0KQCjEpxcIr5D+Yv90plTitZt9JBfQvVJDS5seMl3FOvsh3+9CoYWXf/1l5OaZzzF6nDm4cagaQ==", "dev": true, "optional": true }, @@ -15674,6 +15841,12 @@ "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", "dev": true }, + "natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true + }, "ncp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/ncp/-/ncp-2.0.0.tgz", @@ -15750,6 +15923,16 @@ "integrity": "sha512-l2NNj07e9afPnhAhvgVrCD/oy2Ai1yfLpuo3EpiO1jFTsB4sFz6oIfAfSZyQzVpkZQ9xS8ZS5g1jCBgq4Hwo0g==", "dev": true }, + "nopt": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-5.0.0.tgz", + "integrity": "sha512-Tbj67rffqceeLpcRXrT7vKAN8CwfPeIBgM7E6iBkmKLV7bEMwpGgYLGv0jACUsECaa/vuxP0IjEont6umdMgtQ==", + "dev": true, + "optional": true, + "requires": { + "abbrev": "1" + } + }, "normalize-package-data": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-3.0.3.tgz", @@ -15783,6 +15966,19 @@ "path-key": "^3.0.0" } }, + "npmlog": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/npmlog/-/npmlog-5.0.1.tgz", + "integrity": "sha512-AqZtDUWOMKs1G/8lwylVjrdYgqA4d9nu8hc+0gzRxlDb1I10+FHBGMXs6aiQHFdCUUlqH99MUMuLfzWDNDtfxw==", + "dev": true, + "optional": true, + "requires": { + "are-we-there-yet": "^2.0.0", + "console-control-strings": "^1.1.0", + "gauge": "^3.0.0", + "set-blocking": "^2.0.0" + } + }, "nth-check": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", @@ -16030,9 +16226,9 @@ "dev": true }, "parse5": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.1.tgz", - "integrity": "sha512-kwpuwzB+px5WUg9pyK0IcK/shltJN5/OVhQagxhCQNtT9Y9QRZqNY2e1cmbu/paRh5LMnz/oVTVLBpjFmMZhSg==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.1.2.tgz", + "integrity": "sha512-Czj1WaSVpaoj0wbhMzLmWD69anp2WH7FXMB9n1Sy8/ZFF9jolSQVMu1Ij5WIyGmcBmhk7EOndpO4mIpihVqAXw==", "dev": true, "requires": { "entities": "^4.4.0" @@ -16106,12 +16302,12 @@ "dev": true }, "pdfjs-dist": { - "version": "2.16.105", - "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-2.16.105.tgz", - "integrity": "sha512-J4dn41spsAwUxCpEoVf6GVoz908IAA3mYiLmNxg8J9kfRXc2jxpbUepcP0ocp0alVNLFthTAM8DZ1RaHh8sU0A==", + "version": "3.1.81", + "resolved": "https://registry.npmjs.org/pdfjs-dist/-/pdfjs-dist-3.1.81.tgz", + "integrity": "sha512-hZHVVbjU2Ac1VYyPFrg9fBcyS7EEdB8YFy5upk6LmnsXl10WxAavdiViGWi2C/xK0GZObEpSSJU1VnoF9t8n9w==", "dev": true, "requires": { - "dommatrix": "^1.0.3", + "canvas": "^2.10.2", "web-streams-polyfill": "^3.2.1" } }, @@ -16145,9 +16341,9 @@ "dev": true }, "pino": { - "version": "8.6.1", - "resolved": "https://registry.npmjs.org/pino/-/pino-8.6.1.tgz", - "integrity": "sha512-fi+V2K98eMZjQ/uEHHSiMALNrz7HaFdKNYuyA3ZUrbH0f1e8sPFDmeRGzg7ZH2q4QDxGnJPOswmqlEaTAZeDPA==", + "version": "8.7.0", + "resolved": "https://registry.npmjs.org/pino/-/pino-8.7.0.tgz", + "integrity": "sha512-l9sA5uPxmZzwydhMWUcm1gI0YxNnYl8MfSr2h8cwLvOAzQLBLewzF247h/vqHe3/tt6fgtXeG9wdjjoetdI/vA==", "dev": true, "requires": { "atomic-sleep": "^1.0.0", @@ -16274,9 +16470,9 @@ "peer": true }, "postcss": { - "version": "8.4.18", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.18.tgz", - "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==", + "version": "8.4.19", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.19.tgz", + "integrity": "sha512-h+pbPsyhlYj6N2ozBmHhHrs9DzGmbaarbLvWipMRO7RLS+v4onj26MPFXA5OBYFxyqYhUJK456SwDcY9H2/zsA==", "requires": { "nanoid": "^3.3.4", "picocolors": "^1.0.0", @@ -16363,9 +16559,9 @@ "dev": true }, "process-warning": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.0.0.tgz", - "integrity": "sha512-+MmoAXoUX+VTHAlwns0h+kFUWFs/3FZy+ZuchkgjyOu3oioLAo2LB5aCfKPh2+P9O18i3m43tUEv3YqttSy0Ww==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/process-warning/-/process-warning-2.1.0.tgz", + "integrity": "sha512-9C20RLxrZU/rFnxWncDkuF6O999NdIf3E1ws4B0ZeY3sRVPzWBMsYDE2lxjxhiXxg464cQTgKUGm8/i6y2YGXg==", "dev": true }, "progress": { @@ -16427,34 +16623,44 @@ } }, "puppeteer": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.1.0.tgz", - "integrity": "sha512-UyJ5gz5JNjuFo6VJzIf+qDNjbSWGSoAMLuW990eErcrH6sZP85EbpLi6yG50euTMudxO/lsj4w1VNDNogHv6dA==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/puppeteer/-/puppeteer-19.3.0.tgz", + "integrity": "sha512-WJbi/ULaeuFOz7cfMgJlJCBAZiyqIFeQ6os4h5ex3PVTt2qosXgwI9eruFZqFAwJRv8x5pOuMhWR0aSRgyDqEg==", "dev": true, "requires": { "cosmiconfig": "7.0.1", + "devtools-protocol": "0.0.1056733", "https-proxy-agent": "5.0.1", "progress": "2.0.3", "proxy-from-env": "1.1.0", - "puppeteer-core": "19.1.0" + "puppeteer-core": "19.3.0" } }, "puppeteer-core": { - "version": "19.1.0", - "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.1.0.tgz", - "integrity": "sha512-xIIJJuvqWbUwNzaB7l0TyChJYHdLvLhcHQiBLLKsMfvaQXnVa0Fzooq3Zb5bc01Q/b7XiP9pqDvUcYWSmzZQHA==", + "version": "19.3.0", + "resolved": "https://registry.npmjs.org/puppeteer-core/-/puppeteer-core-19.3.0.tgz", + "integrity": "sha512-P8VAAOBnBJo/7DKJnj1b0K9kZBF2D8lkdL94CjJ+DZKCp182LQqYemPI9omUSZkh4bgykzXjZhaVR1qtddTTQg==", "dev": true, "requires": { "cross-fetch": "3.1.5", "debug": "4.3.4", - "devtools-protocol": "0.0.1045489", + "devtools-protocol": "0.0.1056733", "extract-zip": "2.0.1", "https-proxy-agent": "5.0.1", "proxy-from-env": "1.1.0", "rimraf": "3.0.2", "tar-fs": "2.1.1", "unbzip2-stream": "1.4.3", - "ws": "8.9.0" + "ws": "8.10.0" + }, + "dependencies": { + "ws": { + "version": "8.10.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.10.0.tgz", + "integrity": "sha512-+s49uSmZpvtAsd2h37vIPy1RBusaLawVe8of+GyEPsaJTCMpj/2v8NpeK1SHXjBlQ95lQTmQofOJnFiLoaN3yw==", + "dev": true, + "requires": {} + } } }, "qs": { @@ -16472,6 +16678,12 @@ "inherits": "~2.0.3" } }, + "queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true + }, "quick-format-unescaped": { "version": "4.0.4", "resolved": "https://registry.npmjs.org/quick-format-unescaped/-/quick-format-unescaped-4.0.4.tgz", @@ -16620,9 +16832,9 @@ } }, "regenerator-runtime": { - "version": "0.13.10", - "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.10.tgz", - "integrity": "sha512-KepLsg4dU12hryUO7bp/axHAKvwGOCV0sGloQtpagJ12ai+ojVDqkeGSiRX1zlq+kjIMZ1t7gpze+26QqtdGqw==", + "version": "0.13.11", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.11.tgz", + "integrity": "sha512-kY1AZVr2Ra+t+piVaJ4gxaFaReZVH40AKNo7UCX6W+dEwBo/2oZJzqfuN1qLq1oL45o56cPaTXELwrTh8Fpggg==", "dev": true }, "regexpp": { @@ -16803,24 +17015,27 @@ } }, "run-parallel": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.9.tgz", - "integrity": "sha512-DEqnSRTDw/Tc3FXf49zedI638Z9onwUotBMiUFKmrO2sdFKIbXamXGQ3Axd4qgphxKB4kw/qP1w5kTxnfU1B9Q==", - "dev": true + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "requires": { + "queue-microtask": "^1.2.2" + } }, "rxjs": { - "version": "7.5.5", - "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.5.tgz", - "integrity": "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw==", + "version": "7.5.7", + "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.5.7.tgz", + "integrity": "sha512-z9MzKh/UcOqB3i20H6rtrlaE/CgjLOvheWK/9ILrbhROGTweAi1BaFsTT9FbwZi5Trr1qNRs+MXkhmR06awzQA==", "dev": true, "requires": { "tslib": "^2.1.0" }, "dependencies": { "tslib": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.0.tgz", - "integrity": "sha512-d6xOpEDfsi2CZVlPQzGeux8XMwLT9hssAsaPYExaQMuYskwb+x1x7J371tWlbBdWHroy99KnVB6qIkUbs5X3UQ==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.4.1.tgz", + "integrity": "sha512-tGyy4dAjRIEwI7BzsB0lynWgOpfqjUdq91XXAlIWD2OwKBH7oCl/GZG/HT4BOHrTlPMOASlMQ7veyTqpmRcrNA==", "dev": true } } @@ -16897,6 +17112,13 @@ "type-fest": "^0.13.1" } }, + "set-blocking": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/set-blocking/-/set-blocking-2.0.0.tgz", + "integrity": "sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw==", + "dev": true, + "optional": true + }, "set-immediate-shim": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", @@ -16975,6 +17197,44 @@ "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "optional": true + }, + "simple-get": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-3.1.1.tgz", + "integrity": "sha512-CQ5LTKGfCpvE1K0n2us+kuMPbk/q0EKl82s4aheV9oXjFEz6W/Y7oQFVJuU6QG77hRT4Ghb5RURteF5vnWjupA==", + "dev": true, + "optional": true, + "requires": { + "decompress-response": "^4.2.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + }, + "dependencies": { + "decompress-response": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-4.2.1.tgz", + "integrity": "sha512-jOSne2qbyE+/r8G1VU+G/82LBs2Fs4LAsTiLSHOCOMZQl2OKZ6i8i4IyHemTe+/yIXOtTcRQMzPcgyhoFlqPkw==", + "dev": true, + "optional": true, + "requires": { + "mimic-response": "^2.0.0" + } + }, + "mimic-response": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-2.1.0.tgz", + "integrity": "sha512-wXqjST+SLt7R009ySCglWBCFpjUygmCIfD790/kVbiGmUgfYGuB14PiTd5DwVxSV4NcYHjzMkoj5LjQZwTQLEA==", + "dev": true, + "optional": true + } + } + }, "slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -17000,9 +17260,9 @@ } }, "sonic-boom": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.0.tgz", - "integrity": "sha512-SbbZ+Kqj/XIunvIAgUZRlqd6CGQYq71tRRbXR92Za8J/R3Yh4Av+TWENiSiEgnlwckYLyP0YZQWVfyNC0dzLaA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/sonic-boom/-/sonic-boom-3.2.1.tgz", + "integrity": "sha512-iITeTHxy3B9FGu8aVdiDXUVAcHMF9Ss0cCsAOo2HfCrmVGT3/DT5oYaeu0M/YKZDlKTvChEyPq0zI9Hf33EX6A==", "dev": true, "requires": { "atomic-sleep": "^1.0.0" @@ -17115,9 +17375,9 @@ } }, "stack-utils": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.5.tgz", - "integrity": "sha512-xrQcmYhOsn/1kX+Vraq+7j4oE2j/6BFscZ0etmYg81xuM8Gq0022Pxb8+IqgOFUIaxHs0KaSb7T1+OegiNrNFA==", + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", + "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "requires": { "escape-string-regexp": "^2.0.0" @@ -17270,15 +17530,15 @@ "dev": true }, "stylelint": { - "version": "14.14.0", - "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.14.0.tgz", - "integrity": "sha512-yUI+4xXfPHVnueYddSQ/e1GuEA/2wVhWQbGj16AmWLtQJtn28lVxfS4b0CsWyVRPgd3Auzi0NXOthIEUhtQmmA==", + "version": "14.15.0", + "resolved": "https://registry.npmjs.org/stylelint/-/stylelint-14.15.0.tgz", + "integrity": "sha512-JOgDAo5QRsqiOZPZO+B9rKJvBm64S0xasbuRPAbPs6/vQDgDCnZLIiw6XcAS6GQKk9k1sBWR6rmH3Mfj8OknKg==", "dev": true, "requires": { "@csstools/selector-specificity": "^2.0.2", "balanced-match": "^2.0.0", "colord": "^2.9.3", - "cosmiconfig": "^7.0.1", + "cosmiconfig": "^7.1.0", "css-functions-list": "^3.1.0", "debug": "^4.3.4", "fast-glob": "^3.2.12", @@ -17292,13 +17552,13 @@ "import-lazy": "^4.0.0", "imurmurhash": "^0.1.4", "is-plain-object": "^5.0.0", - "known-css-properties": "^0.25.0", + "known-css-properties": "^0.26.0", "mathml-tag-names": "^2.1.3", "meow": "^9.0.0", "micromatch": "^4.0.5", "normalize-path": "^3.0.0", "picocolors": "^1.0.0", - "postcss": "^8.4.17", + "postcss": "^8.4.19", "postcss-media-query-parser": "^0.2.3", "postcss-resolve-nested-selector": "^0.1.1", "postcss-safe-parser": "^6.0.0", @@ -17310,7 +17570,7 @@ "style-search": "^0.1.0", "supports-hyperlinks": "^2.3.0", "svg-tags": "^1.0.0", - "table": "^6.8.0", + "table": "^6.8.1", "v8-compile-cache": "^2.3.0", "write-file-atomic": "^4.0.2" }, @@ -17321,6 +17581,19 @@ "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==", "dev": true }, + "cosmiconfig": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-7.1.0.tgz", + "integrity": "sha512-AdmX6xUzdNASswsFtmwSt7Vj8po9IuqXm0UXz7QKPuEUmPB4XyjGfaAr2PSuELMwkRMVH1EpIkX5bTZGRB3eCA==", + "dev": true, + "requires": { + "@types/parse-json": "^4.0.0", + "import-fresh": "^3.2.1", + "parse-json": "^5.0.0", + "path-type": "^4.0.0", + "yaml": "^1.10.0" + } + }, "strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", @@ -17430,14 +17703,14 @@ "dev": true }, "sweetalert2": { - "version": "11.4.33", - "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.4.33.tgz", - "integrity": "sha512-gnXH7c7xFzs9BtmHBVH4Fl/cEKhOhf4gv6UuZpFqUoFWtwIKG5sjNkcDNYW+8v3wyFestLab5qI4i1H1MkN0Pg==" + "version": "11.6.8", + "resolved": "https://registry.npmjs.org/sweetalert2/-/sweetalert2-11.6.8.tgz", + "integrity": "sha512-0YHMaqF3DC67EI9uZzHpbU34rQV3acEFlnUCYmDSDNkeNOSFtSlF4DbWilfln+iUYv9s9aqbREXmKZRJqh5G5w==" }, "table": { - "version": "6.8.0", - "resolved": "https://registry.npmjs.org/table/-/table-6.8.0.tgz", - "integrity": "sha512-s/fitrbVeEyHKFa7mFdkuQMWlH1Wgw/yEXMt5xACT4ZpzWFluehAxRtUUQKPuWhaLAWhFcVx6w3oC8VKaUfPGA==", + "version": "6.8.1", + "resolved": "https://registry.npmjs.org/table/-/table-6.8.1.tgz", + "integrity": "sha512-Y4X9zqrCftUhMeH2EptSSERdVKt/nEdijTOacGD/97EKjhQ/Qs8RTlEGABSJNNN8lac9kheH+af7yAkEWlgneA==", "dev": true, "requires": { "ajv": "^8.0.1", @@ -17448,9 +17721,9 @@ }, "dependencies": { "ajv": { - "version": "8.10.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.10.0.tgz", - "integrity": "sha512-bzqAEZOjkrUMl2afH8dknrq5KEk2SrwdBROR+vH1EKVQTqaUbJVPdc/gEdggTMM0Se+s+Ja4ju4TlNcStKl2Hw==", + "version": "8.11.2", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.11.2.tgz", + "integrity": "sha512-E4bfmKAhGiSTvMfL1Myyycaub+cUEU2/IvpylXkUu7CHBkBj1f/ikdzbD7YQ6FKUbixDxeYvB/xY4fvyroDlQg==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -17517,6 +17790,30 @@ } } }, + "tar": { + "version": "6.1.12", + "resolved": "https://registry.npmjs.org/tar/-/tar-6.1.12.tgz", + "integrity": "sha512-jU4TdemS31uABHd+Lt5WEYJuzn+TJTCBLljvIAHZOz6M9Os5pJ4dD+vRFLxPa/n3T0iEFzpi+0x1UfuDZYbRMw==", + "dev": true, + "optional": true, + "requires": { + "chownr": "^2.0.0", + "fs-minipass": "^2.0.0", + "minipass": "^3.0.0", + "minizlib": "^2.1.1", + "mkdirp": "^1.0.3", + "yallist": "^4.0.0" + }, + "dependencies": { + "chownr": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", + "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", + "dev": true, + "optional": true + } + } + }, "tar-fs": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", @@ -17543,9 +17840,9 @@ } }, "temp-dir": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-2.0.0.tgz", - "integrity": "sha512-aoBAniQmmwtcKp/7BzsH8Cxzv8OL736p7v1ihGb5e9DJ9kTwGWHrQrVB5+lfVDzfGrdRzXch+ig7LHaY1JTOrg==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/temp-dir/-/temp-dir-3.0.0.tgz", + "integrity": "sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==", "dev": true }, "text-table": { @@ -17764,9 +18061,9 @@ } }, "typescript": { - "version": "4.8.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.8.4.tgz", - "integrity": "sha512-QCh+85mCy+h0IGff8r5XWzOVSbBO+KfeYrMQh7NJ58QujwcE22u+NUSmUxqF+un70P9GXKxa2HCNiTTMJknyjQ==", + "version": "4.9.4", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.4.tgz", + "integrity": "sha512-Uz+dTXYzxXXbsFpM86Wh3dKCxrQqUcVMxwU54orwlJjOpO3ao8L7j5lH+dWfTwgCwIuM9GQ2kvVotzYJMXTBZg==", "dev": true }, "uc.micro": { @@ -17918,14 +18215,14 @@ } }, "web-ext": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.3.1.tgz", - "integrity": "sha512-ZTfktd1zcQpWaFAM3U+IQW674G89d1IW/Oh0Ncw9LwFvKvAcW/nA5EB4pwqB8LiW/6OSYQhHBP4x2XUTBu1SKg==", + "version": "7.4.0", + "resolved": "https://registry.npmjs.org/web-ext/-/web-ext-7.4.0.tgz", + "integrity": "sha512-dT2HJaGNXxRNuOtzaVBtEULccL0kM2SN1ark1NnN/ZSlbucobBxCDj6119iki72YyuXpaXZCJGqfZtVf1Znocg==", "dev": true, "requires": { - "@babel/runtime": "7.19.4", + "@babel/runtime": "7.20.1", "@devicefarmer/adbkit": "3.2.3", - "addons-linter": "5.18.0", + "addons-linter": "5.23.0", "bunyan": "1.8.15", "camelcase": "7.0.0", "chrome-launcher": "0.15.1", @@ -17936,11 +18233,11 @@ "fs-extra": "10.1.0", "fx-runner": "1.3.0", "import-fresh": "3.3.0", - "jose": "4.10.0", + "jose": "4.11.1", "mkdirp": "1.0.4", "multimatch": "6.0.0", "mz": "2.7.0", - "node-fetch": "3.2.10", + "node-fetch": "3.3.0", "node-notifier": "10.0.1", "open": "8.4.0", "parse-json": "6.0.2", @@ -17952,8 +18249,8 @@ "tmp": "0.2.1", "update-notifier": "6.0.2", "watchpack": "2.4.0", - "ws": "8.9.0", - "yargs": "17.6.0", + "ws": "8.11.0", + "yargs": "17.6.2", "zip-dir": "2.0.0" }, "dependencies": { @@ -17986,9 +18283,9 @@ "dev": true }, "node-fetch": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.2.10.tgz", - "integrity": "sha512-MhuzNwdURnZ1Cp4XTazr69K0BTizsBroX7Zx3UgDSVcZYKF/6p0CBe4EUb/hLqmzVhl0UpYfgRljQ4yxE+iCxA==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.0.tgz", + "integrity": "sha512-BKwRP/O0UvoMKp7GNdwPlObhYGB5DQqwhEDQlNKuoqwVYSxkSZCSbHjnFFmUEtwSKRPU4kNK8PbDYYitwaE3QA==", "dev": true, "requires": { "data-uri-to-buffer": "^4.0.0", @@ -18065,6 +18362,16 @@ "isexe": "^2.0.0" } }, + "wide-align": { + "version": "1.1.5", + "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.5.tgz", + "integrity": "sha512-eDMORYaPNZ4sQIuuYPDHdQvf4gyCF9rEEV/yPxGfwPkRodwEgiMUUXTx/dex+Me0wxx53S+NgUHaP7y3MGlDmg==", + "dev": true, + "optional": true, + "requires": { + "string-width": "^1.0.2 || 2 || 3 || 4" + } + }, "widest-line": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/widest-line/-/widest-line-4.0.1.tgz", @@ -18152,9 +18459,9 @@ "dev": true }, "ws": { - "version": "8.9.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.9.0.tgz", - "integrity": "sha512-Ja7nszREasGaYUYCI2k4lCKIRTt+y7XuqVoHR44YpI49TtryyqbqvDMn5eqfW7e6HzTukDRIsXqzVHScqRcafg==", + "version": "8.11.0", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.11.0.tgz", + "integrity": "sha512-HPG3wQd9sNQoT9xHyNCXoDUa+Xw/VevmY9FoHyQ+g+rrMn4j6FB4np7Z0OhdTgjx6MgQLK7jwSy1YecU1+4Asg==", "dev": true, "requires": {} }, @@ -18199,9 +18506,9 @@ "dev": true }, "yargs": { - "version": "17.6.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.0.tgz", - "integrity": "sha512-8H/wTDqlSwoSnScvV2N/JHfLWOKuh5MVla9hqLjK3nsfyy6Y4kDSYSvkU5YCUEPOSnRXfIyx3Sq+B/IWudTo4g==", + "version": "17.6.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.6.2.tgz", + "integrity": "sha512-1/9UrdHjDZc0eOU0HxOHoS78C69UD3JRMvzlJ7S79S2nTaWRA/whGCTV8o9e/N/1Va9YIV7Q4sOxD8VV4pCWOw==", "dev": true, "requires": { "cliui": "^8.0.1", @@ -18210,13 +18517,13 @@ "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^21.0.0" + "yargs-parser": "^21.1.1" }, "dependencies": { "yargs-parser": { - "version": "21.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.0.1.tgz", - "integrity": "sha512-9BK1jFpLzJROCI5TzwZL/TU4gqjK5xiHV/RfWLOahrjAko/e4DJkRDZQXfvqAsiZzzYhgAzbgz6lg48jcm4GLg==", + "version": "21.1.1", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", + "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true } } @@ -18267,4 +18574,4 @@ "integrity": "sha1-KOwXzwl0PtyrBW3dixsGJizHPDA=" } } -} +} \ No newline at end of file diff --git a/package.json b/package.json index c62e069b078..b3e9a8a276d 100644 --- a/package.json +++ b/package.json @@ -1,57 +1,57 @@ { "name": "flowcrypt-browser", - "version": "8.3.8", + "version": "8.4.0", "description": "Simple end-to-end encryption to secure email and attachments on Google.", "resolutions": { "graceful-fs": "4.1.13" }, "devDependencies": { "@openpgp/web-stream-tools": "^0.0.11", - "@types/chai": "4.3.3", + "@types/chai": "4.3.4", "@types/chai-as-promised": "7.1.5", - "@types/chrome": "0.0.199", - "@types/dompurify": "2.3.4", + "@types/chrome": "0.0.203", + "@types/dompurify": "2.4.0", "@types/jquery": "3.5.14", "@types/mailparser": "3.4.0", "@types/mkdirp": "1.0.2", "@types/request": "2.48.8", - "@typescript-eslint/eslint-plugin": "5.40.1", - "@typescript-eslint/parser": "5.40.1", - "ava": "5.0.1", - "chai": "4.3.6", + "@typescript-eslint/eslint-plugin": "5.45.0", + "@typescript-eslint/parser": "5.45.0", + "ava": "5.1.0", + "chai": "4.3.7", "chai-as-promised": "7.1.1", "del": "7.0.0", - "eslint": "8.24.0", + "eslint": "8.28.0", "eslint-plugin-header": "3.1.1", "eslint-plugin-no-only-tests": "3.1.0", "fc-node-requests": "git+https://git@github.com/FlowCrypt/node-requests.git", - "googleapis": "108.0.0", - "husky": "^8.0.1", - "lint-staged": "^13.0.3", + "googleapis": "109.0.1", + "husky": "^8.0.2", + "lint-staged": "^13.0.4", "mailparser": "3.5.0", "mkdirp": "1.0.4", "openpgp": ">=5.5.0", - "pdfjs-dist": "2.16.105", - "puppeteer": "19.1.0", - "stylelint": "14.14.0", + "pdfjs-dist": "3.1.81", + "puppeteer": "19.3.0", + "stylelint": "14.15.0", "stylelint-config-standard": "29.0.0", "tslint": "6.1.3", "tsutils": "^3.21.0", - "typescript": "4.8.4", - "web-ext": "7.3.1" + "typescript": "4.9.4", + "web-ext": "7.4.0" }, "dependencies": { "@flowcrypt/fine-uploader": "5.16.4", "bootstrap": "4.6.2", "clipboard": "2.0.11", - "dompurify": "2.4.0", + "dompurify": "2.4.1", "filesize": "10.0.5", "iso-8859-2": "1.0.0", "jquery": "3.6.1", "node-forge": "1.3.1", "postcss-html": "^1.5.0", "squire-rte": "1.11.3", - "sweetalert2": "11.4.33", + "sweetalert2": "11.6.8", "zxcvbn": "4.4.2" }, "scripts": { diff --git a/test/source/mock/backend/backend-data.ts b/test/source/mock/backend/backend-data.ts index 85ea136fdd9..85c2f32fafa 100644 --- a/test/source/mock/backend/backend-data.ts +++ b/test/source/mock/backend/backend-data.ts @@ -104,6 +104,12 @@ export class BackendData { ] }; } + if (domain === 'custom-sks.flowcrypt.test') { + return { + ...keyManagerAutogenRules, + custom_keyserver_url: 'https://localhost:8001' + }; + } if (domain === 'forbid-storing-passphrase-client-configuration.flowcrypt.test') { return { "flags": [ @@ -143,7 +149,7 @@ export class BackendData { "allow_attester_search_only_for_domains": [] }; } - if (domain === 'google.mock.flowcryptlocal.test:8001') { + if (domain === 'google.mock.localhost:8001') { return { ...keyManagerAutogenRules, flags: [...keyManagerAutogenRules.flags, 'NO_ATTESTER_SUBMIT'] }; } if (domain === 'key-manager-autogen.flowcrypt.test') { @@ -152,6 +158,9 @@ export class BackendData { if (domain === 'key-manager-autoimport-no-prv-create.flowcrypt.test') { return { ...keyManagerAutogenRules, flags: [...keyManagerAutogenRules.flags, 'NO_PRV_CREATE'] }; } + if (domain === 'key-manager-disabled-password-message.flowcrypt.test') { + return { ...keyManagerAutogenRules, flags: [...keyManagerAutogenRules.flags, 'DISABLE_FLOWCRYPT_HOSTED_PASSWORD_MESSAGES'] }; + } if (domain === 'key-manager-autoimport-no-prv-create-no-attester-submit.flowcrypt.test') { return { ...keyManagerAutogenRules, flags: [...keyManagerAutogenRules.flags, 'NO_PRV_CREATE', 'NO_ATTESTER_SUBMIT'] }; } diff --git a/test/source/mock/backend/backend-endpoints.ts b/test/source/mock/backend/backend-endpoints.ts index 63291e7b235..dba6d88e1a9 100644 --- a/test/source/mock/backend/backend-endpoints.ts +++ b/test/source/mock/backend/backend-endpoints.ts @@ -14,11 +14,7 @@ export const mockBackendData = new BackendData(); export const mockBackendEndpoints: HandlersDefinition = { '/api/account/get': async ({ }, req) => { throwIfNotPost(req); - const idToken = req.headers.authorization?.replace(/^Bearer /, ''); - if (!idToken) { - throw new HttpClientErr('backend mock: Missing id_token'); - } - const email = oauth.extractEmailFromIdToken(idToken); + const email = getEmailFromIdTokenOrThrow(req); return JSON.stringify({ account: mockBackendData.getAcctRow(email!), domain_org_rules: mockBackendData.getClientConfiguration(email!), @@ -41,7 +37,8 @@ export const mockBackendEndpoints: HandlersDefinition = { expect((body as { email: string }).email).to.equal('flowcrypt.compatibility@gmail.com'); return { sent: true, text: 'Feedback sent' }; }, - '/api/message/upload': async ({ }) => { + '/api/message/upload': async ({ }, req) => { + getEmailFromIdTokenOrThrow(req); return { short: 'mockmsg000' }; }, '/api/link/me': async ({ }, req) => { @@ -53,4 +50,16 @@ const throwIfNotPost = (req: IncomingMessage) => { if (!isPost(req)) { throw new HttpClientErr('Backend mock calls must use POST method'); } +}; + +const getEmailFromIdTokenOrThrow = (req: IncomingMessage) => { + const idToken = req.headers.authorization?.replace(/^Bearer /, ''); + if (!idToken) { + throw new HttpClientErr('backend mock: Missing id_token'); + } + const email = oauth.extractEmailFromIdToken(idToken); + if (!email) { + throw new HttpClientErr('Invalid Id token. Missing email.'); + } + return email; }; \ No newline at end of file diff --git a/test/source/mock/fes/fes-endpoints.ts b/test/source/mock/fes/fes-endpoints.ts index d56ab6098d1..cb135b2fb21 100644 --- a/test/source/mock/fes/fes-endpoints.ts +++ b/test/source/mock/fes/fes-endpoints.ts @@ -8,7 +8,7 @@ import { HandlersDefinition } from '../all-apis-mock'; import { HttpClientErr, Status } from '../lib/api'; import { MockJwt } from '../lib/oauth'; -const standardFesUrl = 'fes.standardsubdomainfes.test:8001'; +const standardFesUrl = 'fes.standardsubdomainfes.localhost:8001'; const issuedAccessTokens: string[] = []; const processMessageFromUser = async (body: string) => { @@ -188,7 +188,7 @@ export const mockFesEndpoints: HandlersDefinition = { // this makes enterprise version tolerate missing FES - explicit 404 throw new HttpClientErr(`Not found`, 404); } - if (req.headers.host === 'fes.google.mock.flowcryptlocal.test:8001') { + if (req.headers.host === 'fes.google.mock.localhost:8001') { // test `compose - auto include pubkey is inactive when our key is available on Wkd` uses this // this makes enterprise version tolerate missing FES - explicit 404 throw new HttpClientErr(`Not found`, 404); @@ -200,7 +200,7 @@ export const mockFesEndpoints: HandlersDefinition = { if (req.method !== 'GET') { throw new HttpClientErr('Unsupported method'); } - if (req.headers.host === standardFesUrl && req.url === `/api/v1/client-configuration?domain=standardsubdomainfes.test:8001`) { + if (req.headers.host === standardFesUrl && req.url === `/api/v1/client-configuration?domain=standardsubdomainfes.localhost:8001`) { return { clientConfiguration: { flags: [], disallow_attester_search_for_domains: ['got.this@fromstandardfes.com'] }, }; @@ -217,18 +217,18 @@ export const mockFesEndpoints: HandlersDefinition = { '/api/v1/message': async ({ body }, req) => { // body is a mime-multipart string, we're doing a few smoke checks here without parsing it if (req.headers.host === standardFesUrl && req.method === 'POST' && typeof body === 'string') { - // test: `compose - user@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal` + // test: `compose - user@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal` authenticate(req, 'oidc'); - if (body.includes('"from":"user@standardsubdomainfes.test:8001"')) { + if (body.includes('"from":"user@standardsubdomainfes.localhost:8001"')) { return await processMessageFromUser(body); } - if (body.includes('"from":"user2@standardsubdomainfes.test:8001"')) { + if (body.includes('"from":"user2@standardsubdomainfes.localhost:8001"')) { return await processMessageFromUser2(body); } - if (body.includes('"from":"user3@standardsubdomainfes.test:8001"')) { + if (body.includes('"from":"user3@standardsubdomainfes.localhost:8001"')) { return await processMessageFromUser3(body); } - if (body.includes('"from":"user4@standardsubdomainfes.test:8001"')) { + if (body.includes('"from":"user4@standardsubdomainfes.localhost:8001"')) { return await processMessageFromUser4(body); } } @@ -236,45 +236,45 @@ export const mockFesEndpoints: HandlersDefinition = { }, '/api/v1/message/FES-MOCK-EXTERNAL-ID/gateway': async ({ body }, req) => { if (req.headers.host === standardFesUrl && req.method === 'POST') { - // test: `compose - user@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal` + // test: `compose - user@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal` authenticate(req, 'oidc'); - expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.test:8001>"}/); + expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.localhost:8001>"}/); return {}; } throw new HttpClientErr('Not Found', 404); }, '/api/v1/message/FES-MOCK-EXTERNAL-FOR-SENDER@DOMAIN.COM-ID/gateway': async ({ body }, req) => { if (req.headers.host === standardFesUrl && req.method === 'POST') { - // test: `compose - user2@standardsubdomainfes.test:8001 - PWD encrypted message with FES - Reply rendering` + // test: `compose - user2@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES - Reply rendering` authenticate(req, 'oidc'); - expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.test:8001>"}/); + expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.localhost:8001>"}/); return {}; } throw new HttpClientErr('Not Found', 404); }, '/api/v1/message/FES-MOCK-EXTERNAL-FOR-TO@EXAMPLE.COM-ID/gateway': async ({ body }, req) => { if (req.headers.host === standardFesUrl && req.method === 'POST') { - // test: `compose - user@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal` - // test: `compose - user2@standardsubdomainfes.test:8001 - PWD encrypted message with FES - Reply rendering` - // test: `compose - user3@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal - pubkey recipient in bcc` - // test: `compose - user4@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal - some sends fail with BadRequest error` + // test: `compose - user@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal` + // test: `compose - user2@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES - Reply rendering` + // test: `compose - user3@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - pubkey recipient in bcc` + // test: `compose - user4@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - some sends fail with BadRequest error` authenticate(req, 'oidc'); - expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.test:8001>"}/); + expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.localhost:8001>"}/); return {}; } throw new HttpClientErr('Not Found', 404); }, '/api/v1/message/FES-MOCK-EXTERNAL-FOR-BCC@EXAMPLE.COM-ID/gateway': async ({ body }, req) => { if (req.headers.host === standardFesUrl && req.method === 'POST') { - // test: `compose - user@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal` + // test: `compose - user@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal` authenticate(req, 'oidc'); - expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.test:8001>"}/); + expect(body).to.match(/{"emailGatewayMessageId":"<(.+)@standardsubdomainfes.localhost:8001>"}/); return {}; } throw new HttpClientErr('Not Found', 404); }, '/api/v1/message/FES-MOCK-EXTERNAL-FOR-GATEWAYFAILURE@EXAMPLE.COM-ID/gateway': async () => { - // test: `user4@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal - a send fails with gateway update error` + // test: `user4@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - a send fails with gateway update error` throw new HttpClientErr(`Test error`, Status.BAD_REQUEST); }, }; diff --git a/test/source/mock/google/exported-messages/message-export-1803be3182d1937b.json b/test/source/mock/google/exported-messages/message-export-1803be3182d1937b.json index 1f85a37dd1f..54690396b43 100644 --- a/test/source/mock/google/exported-messages/message-export-1803be3182d1937b.json +++ b/test/source/mock/google/exported-messages/message-export-1803be3182d1937b.json @@ -1,5 +1,5 @@ { - "acctEmail": "user2@standardsubdomainfes.test:8001", + "acctEmail": "user2@standardsubdomainfes.localhost:8001", "full": { "id": "1803be3182d1937b", "threadId": "1803be2e506153d2", diff --git a/test/source/mock/google/exported-messages/message-export-184a474fc1bd59b8.json b/test/source/mock/google/exported-messages/message-export-184a474fc1bd59b8.json new file mode 100644 index 00000000000..9cc0a4f9bd9 --- /dev/null +++ b/test/source/mock/google/exported-messages/message-export-184a474fc1bd59b8.json @@ -0,0 +1,153 @@ +{ + "acctEmail": "ci.tests.gmail@flowcrypt.test", + "full": { + "id": "184a474fc1bd59b8", + "threadId": "184a474fc1bd59b8", + "labelIds": [ + "IMPORTANT", + "CATEGORY_PERSONAL", + "INBOX" + ], + "snippet": "", + "payload": { + "partId": "", + "mimeType": "multipart/mixed", + "filename": "", + "headers": [ + { + "name": "X-Gm-Message-State", + "value": "ANoB5plwy6XI7jdoh8thHiyNuVIABUkhrlVg4ydHiwoNb3mi0xZwX8fd uRMtem74UVXlNQRE6v26o2mre1Ezy/UuEcD1bgnAvt6lSxx0Zg==" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "From", + "value": "sender@domain.com" + }, + { + "name": "Date", + "value": "Wed, 23 Nov 2022 08:27:08 -0400" + }, + { + "name": "Subject", + "value": "encrypted text inside \"message\" attachment" + }, + { + "name": "To", + "value": "flowcrypt.compatibility@gmail.com" + }, + { + "name": "Content-Type", + "value": "multipart/mixed; boundary=\"000000000000f15e0805ee226866\"" + } + ], + "body": { + "size": 0 + }, + "parts": [ + { + "partId": "0", + "mimeType": "multipart/alternative", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "multipart/alternative; boundary=\"000000000000f15e0705ee226864\"" + } + ], + "body": { + "size": 0 + }, + "parts": [ + { + "partId": "0.0", + "mimeType": "text/plain", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "text/plain; charset=\"UTF-8\"" + } + ], + "body": { + "size": 2, + "data": "DQo=" + } + }, + { + "partId": "0.1", + "mimeType": "text/html", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=\"UTF-8\"" + } + ], + "body": { + "size": 27, + "data": "PGRpdiBkaXI9Imx0ciI-PGJyPjwvZGl2Pg0K" + } + } + ] + }, + { + "partId": "1", + "mimeType": "application/octet-stream", + "filename": "message", + "headers": [ + { + "name": "Content-Type", + "value": "application/octet-stream; name=message" + }, + { + "name": "Content-Disposition", + "value": "attachment; filename=message" + }, + { + "name": "Content-Transfer-Encoding", + "value": "base64" + }, + { + "name": "Content-ID", + "value": "" + }, + { + "name": "X-Attachment-Id", + "value": "f_latmek3c0" + } + ], + "body": { + "attachmentId": "ANGjdJ-661X09-S0AGJMLZgT7MpDhPMh24xtFt_nNjabV1mqvfdOWuapiYsQB_Te5JwKaEzwmR_sfpFbv8DRSRyBfxFSxQA49ID2ZRlvegPM1yUDW6ibVA58wImRDhKi_3k6b9wcYBM8UBjZkjfXnEYnMYxSPGd40x6Vq-MElIz2cNf95mjiGosbJAzRgx7hy5yvCB5g-QFoJmCCjicJUDUhz4JcTk9om1XnDHEdKTHwT8pnUK0J9xq8UOR_wqSxtEkwG5oTtybiTBIzPBxqa3rHm18E81O9GkaKLA7xrAvCwaP73d2u-M3Xa5glpJGU-KMLsPZxcYmsXOfMld1tuHX4x-lsk99950zt1OiE5v43niaEla2adcT5pR8AaFT5TA5oETLW0PKoGvz4GGxZ", + "size": 1853 + } + } + ] + }, + "sizeEstimate": 8015, + "historyId": "2420926", + "internalDate": "1669206428000" + }, + "attachments": { + "ANGjdJ-661X09-S0AGJMLZgT7MpDhPMh24xtFt_nNjabV1mqvfdOWuapiYsQB_Te5JwKaEzwmR_sfpFbv8DRSRyBfxFSxQA49ID2ZRlvegPM1yUDW6ibVA58wImRDhKi_3k6b9wcYBM8UBjZkjfXnEYnMYxSPGd40x6Vq-MElIz2cNf95mjiGosbJAzRgx7hy5yvCB5g-QFoJmCCjicJUDUhz4JcTk9om1XnDHEdKTHwT8pnUK0J9xq8UOR_wqSxtEkwG5oTtybiTBIzPBxqa3rHm18E81O9GkaKLA7xrAvCwaP73d2u-M3Xa5glpJGU-KMLsPZxcYmsXOfMld1tuHX4x-lsk99950zt1OiE5v43niaEla2adcT5pR8AaFT5TA5oETLW0PKoGvz4GGxZ": { + "data": "LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEZsb3dDcnlwdCA1LjAuNCBHbWFpbCBFbmNyeXB0aW9uIGZsb3djcnlwdC5jb20KQ29tbWVudDogU2VhbWxlc3NseSBzZW5kLCByZWNlaXZlIGFuZCBzZWFyY2ggZW5jcnlwdGVkIGVtYWlsCgp3Y0ZNQStBRHYvNXY0UmdLQVEvOEN2Z2xKdzNTeTB1TkpkMGhNcVh3RHBtb3g2bzRwZys1NW8xOEpGOXMKeTdPVFNacUVvYUtXbERLYm9mN3lkVjhMSVF4V2gyNTBCRk8wVy9JQ1dySlNkOFErcE0zdGtZcWEyVVpZCkZ1UmpGZ0ROeUZvT2c0TGFaOVJGbzRnSDIrR1krL0V6a1NzNGFBdmlYQzRtNVZvaEFnVnlMTEZVeWpLNgpsRS8wSWlxelJSZkMvME9mMGJPSmJ6QzRmKzZCMmIwSnFZTjNTOERFMEc1NVlHWDFFbjEva0hPY0lQWlgKekxLa2NBZWZOOWtqYTQxa2JkaUJDU244V0hUK1JEalcxc2R4WCs4bTJSUi9FWDFoT25uem4xSlNGZmVyCmR2M3FoSElvaWxqZXh3Y0ZROEFrQldPZkRTM2R2anhCa24zSjY4blNqZGs0azA4VERCNmR6b1hiZFpkWAoyeUMzMWo3ZDI3YW1ZL2lvM0x2Zm9TTzNIRGVUMDZmT0xkRDNXaWhTRXNrbVNSd0cvaWF6UmZCZ09STlEKNVJwa1I1SGs2bE5qVlp1ZnU3bXVJM2VETjNvOHpwSUdmQUh5N01VcFlaNkE3VzJRNm8yRkFxcmxsQVpwCnNBR2g1VUJ3ZERsOThybDBJOVZwUmZndVZWR0xxYzJwV3ozUlIzaEdLZzhQRE9ZMTU1anJzYW01QlN6ZAp6VFg0Njd3UXoxWFc1MlZ0ellXd3IwYTVZSUtkRzRRdmtKOVc5OU5IMlQ3clk0NCtFNHB3eHJVQ2MzcUMKeEVjV09sYitRbGEvK3k5eUczNEtGbCtKQkZXQ1hPUHl2ZmVxeTVmNUdjdDl5amtyUlY4YUJ3RFdDQThvClBkajJnaXFkNG5NajI0Y29NWi9mSTdWbG0rWnU2MDhxUjVVMGJEb3lDWDdCd1V3RFMxb3YvT1l0bFFFQgpFQUNBSk52bHFKTXExNDFtbDB4TzBubEc2UitlSzhLQnA4emllWVhWWndpbUdXZnhqeThDV0xyamJIcjQKMWh5SkJ3TWliV0hRRVpjaEJOM1VXK3ArYjNHTFVOaDBrOUsrYnRrOGd1dFVnTUk2aTdpOE5GaWVVcDNnCm9wZk1UNnI3UFNteGN2Yk9lRmlObmc3OExLaWUxRTVtNmxmMC9zYWhCWUtvRmJFbGhCRFljWnZ4SGFMZApWNStNMFY4TmJLNTFMbVlIeEc5SFFFWlNyeWJHWDljd2w2TjZLWGRFcG5JYThKWGdHT0MrdkVFS0JtNTcKQ3dWVkdPb3EvYjBwSWR5RjlGRGM5T1hxbEVTN21BZ3M4cUROZld5M1pJbGhFak5FZlIwS2dhT2xLay82Cms1eTNLbmhhb0thSlU5UVY4SWhMVFprckMzNnpRdFlJdENOcDFYeU9tYm9MTnBDUEo4NjBFa3VIanhwUwo3MnF0SjFocVI0clk2emY3Y3FsUkt5SkNXRmNILzJyOG9oSTZQSFNKNGVWckZBUWljL2RDWHJWVkVUSE8KVnE3ZUJrLzFTV2FLSllUVStWazF0S0RKUlpYV2ZLUDhrSEdtOG94bmd3amNPV2ZBRWlzUUFieWJ0cW5ZCkN4WC9nK2VFMFFlVjBNVDAzQnBNbWFTS29IOFYvYmdTTTRJMTkzTHR2UWpuS1hVb3NLMnlnRU00MjM3SQozd0lkUm5zRkQ5bGEzR0doZmZKTUd0YlNJZFozb0tVbmVycXpDdlN3elM1K0J3TG4xQ0QwWUN6bHVOTjQKb0F2Z2pHSVkyNklGaE9EOHhxREUzZFZvdUlYSVptMkluUEg0cUNvQlVEYjJ0N3dYSjVMWER2OVd3LzI5ClNzZmdHaS9ORzVtV1BQaXZwQmJudmJNMU50SzBBWDF6Y2JTZGNkU1NJNjkvcUdCZkY5VWtMSHRHZTZ4ZQprVVY5aVphejR6NVpFK050V0FUd1EvaG42eVF4WDBEMWFUay9JLzNzdCtQU25OVjJMT3RHMEQyRGJ2WUYKMnNOZURucithSXRHWkdNNDh6dHNhdnU1YTN4TUx4ZytEUG91RlJtaituSEhCcFFlNDYzREFIYURqM0lICmJhS1lWUWRXZllEWUJVc09lQmF6Z1hMaThnVW9PeE42azkrRWNQeUh1cHk4S1VZRE5uaUdvY2xISGwzYwpBdW5TUXpyMlByU21KK2tyeHdOZFVjamxPZkpwCj1ZVDJBCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0", + "size": 1853 + } + }, + "raw": { + "id": "184a474fc1bd59b8", + "threadId": "184a474fc1bd59b8", + "labelIds": [ + "IMPORTANT", + "CATEGORY_PERSONAL", + "INBOX" + ], + "snippet": "", + "sizeEstimate": 8015, + "raw": "RGVsaXZlcmVkLVRvOiBjaS50ZXN0cy5nbWFpbEBmbG93Y3J5cHQuZGV2DQpSZWNlaXZlZDogYnkgMjAwMjphMDU6NzEwODo2ODgxOjA6MDowOjAgd2l0aCBTTVRQIGlkIG0xY3NwMzAzNDM5NmdkZDsNCiAgICAgICAgV2VkLCAyMyBOb3YgMjAyMiAwNDoyNzoyMSAtMDgwMCAoUFNUKQ0KWC1SZWNlaXZlZDogYnkgMjAwMjphMTc6OTAyOmNjY2U6YjA6MTg1OjQ4ODA6OTFjZCB3aXRoIFNNVFAgaWQgejE0LTIwMDIwYTE3MDkwMmNjY2UwMGIwMDE4NTQ4ODA5MWNkbXI4NTE3MTMxcGxlLjEzMC4xNjY5MjA2NDQxMjY5Ow0KICAgICAgICBXZWQsIDIzIE5vdiAyMDIyIDA0OjI3OjIxIC0wODAwIChQU1QpDQpBUkMtU2VhbDogaT0xOyBhPXJzYS1zaGEyNTY7IHQ9MTY2OTIwNjQ0MTsgY3Y9bm9uZTsNCiAgICAgICAgZD1nb29nbGUuY29tOyBzPWFyYy0yMDE2MDgxNjsNCiAgICAgICAgYj1xZW4reVZqZmRsSHJ3NUlEbmRhNmVQVjhhR0ZWanBVd1NOakt2cUgxSkxBeVRnOHpaVEtIZWJ3R1E1NVFJRWlYVUcNCiAgICAgICAgIGpBOEZuY0tLL0dHdWRmcTNWZUpwaSsvSklmT0Q1V0dSRDhweEZIS21iYUhNMmVEc3lyelNVQnZ4bmVvZy9vQmdjdVBrDQogICAgICAgICBFajR3cUVxWjJvWFpCR3IzbjJXNVJ2TTU2c1Ira3Q3U3NMYldPZFBEME9ORkszMWVPUzRjY2NuQktpSkRwV1llRnNKVg0KICAgICAgICAgRFUzeks2eHRoZFZ2M3BFSXpWVWdZc3VKRVg1bzRKWXJRb2liTUFCeFV5M0xKTGZ5amVZSG1YS0JNY2pnMTV5Nnc3WG8NCiAgICAgICAgIEovMEtqUi92VWtJSUlaamNZNC94UnJOQjBXbEtTcWhEUkZPc1U0V0M4U1hxb3N1bHVyRTYzTDBkK3diMjhGemtVWG9XDQogICAgICAgICBhdU9RPT0NCkFSQy1NZXNzYWdlLVNpZ25hdHVyZTogaT0xOyBhPXJzYS1zaGEyNTY7IGM9cmVsYXhlZC9yZWxheGVkOyBkPWdvb2dsZS5jb207IHM9YXJjLTIwMTYwODE2Ow0KICAgICAgICBoPXRvOnN1YmplY3Q6bWVzc2FnZS1pZDpkYXRlOmZyb206bWltZS12ZXJzaW9uOmRraW0tc2lnbmF0dXJlOw0KICAgICAgICBiaD1IY2hsVTNoQTl1RTRNcGtZbjRURGhuYjlFZmpzMEFpbmFET0xuRHphODZFPTsNCiAgICAgICAgYj1OQ2NGK1ZDdEJCUEsrb0ZaRzc1bGk2eTkzNTJiVHMzSmcralBRNXRzYlR3dVAwc2xzampTNk5CMytrY3ducEl3cE0NCiAgICAgICAgIHdOTTVMYVdja2tJZmxLV3RyOU55SGhiU05SY254eHZJYms1d013b0poTnh6Y0E1OTBxZ1dld1VSV1YrSmFqS3k5VGUwDQogICAgICAgICBQM2pyL3hCVFpxV0VWYnJLNXpLVUFzTDkvc3Vwa1ZBc3Y5RUx6TkFjcXNxek5LdDNjc29WMmc0SEp5dXVhYjJ6TldyUw0KICAgICAgICAgdGNWQUtkd1FZaDczK0dVdnJkWDdaMWoyYkZoVCsyZE83TTJydHlmd1p1akg3UjRqOUNrTUJPQ01NeUJQNkxoaHcxc3cNCiAgICAgICAgIDFwNURoUXRMNHY0czM0WmViRW5vcHdMN2owbVU2Y1pLdTEzREQ3QXpXdlhzM2t5dkRuc0NpWjdDNE0xbHRyK1VoUjB4DQogICAgICAgICAvMTR3PT0NCkFSQy1BdXRoZW50aWNhdGlvbi1SZXN1bHRzOiBpPTE7IG14Lmdvb2dsZS5jb207DQogICAgICAgZGtpbT1wYXNzIGhlYWRlci5pPUBnbWFpbC5jb20gaGVhZGVyLnM9MjAyMTAxMTIgaGVhZGVyLmI9R25tZkd2cGM7DQogICAgICAgc3BmPXBhc3MgKGdvb2dsZS5jb206IGRvbWFpbiBvZiBmbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb20gZGVzaWduYXRlcyAyMDkuODUuMjIwLjQxIGFzIHBlcm1pdHRlZCBzZW5kZXIpIHNtdHAubWFpbGZyb209Zmxvd2NyeXB0LmNvbXBhdGliaWxpdHlAZ21haWwuY29tOw0KICAgICAgIGRtYXJjPXBhc3MgKHA9Tk9ORSBzcD1RVUFSQU5USU5FIGRpcz1OT05FKSBoZWFkZXIuZnJvbT1nbWFpbC5jb20NClJldHVybi1QYXRoOiA8Zmxvd2NyeXB0LmNvbXBhdGliaWxpdHlAZ21haWwuY29tPg0KUmVjZWl2ZWQ6IGZyb20gbWFpbC1zb3ItZjQxLmdvb2dsZS5jb20gKG1haWwtc29yLWY0MS5nb29nbGUuY29tLiBbMjA5Ljg1LjIyMC40MV0pDQogICAgICAgIGJ5IG14Lmdvb2dsZS5jb20gd2l0aCBTTVRQUyBpZCBoNy0yMDAyMGE2MmI0MDcwMDAwMDBiMDA1NjA3NTY5MWZkM3NvcjkxNDE5OTRwZm4uMC4yMDIyLjExLjIzLjA0LjI3LjIxDQogICAgICAgIGZvciA8Y2kudGVzdHMuZ21haWxAZmxvd2NyeXB0LmRldj4NCiAgICAgICAgKEdvb2dsZSBUcmFuc3BvcnQgU2VjdXJpdHkpOw0KICAgICAgICBXZWQsIDIzIE5vdiAyMDIyIDA0OjI3OjIxIC0wODAwIChQU1QpDQpSZWNlaXZlZC1TUEY6IHBhc3MgKGdvb2dsZS5jb206IGRvbWFpbiBvZiBmbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb20gZGVzaWduYXRlcyAyMDkuODUuMjIwLjQxIGFzIHBlcm1pdHRlZCBzZW5kZXIpIGNsaWVudC1pcD0yMDkuODUuMjIwLjQxOw0KQXV0aGVudGljYXRpb24tUmVzdWx0czogbXguZ29vZ2xlLmNvbTsNCiAgICAgICBka2ltPXBhc3MgaGVhZGVyLmk9QGdtYWlsLmNvbSBoZWFkZXIucz0yMDIxMDExMiBoZWFkZXIuYj1Hbm1mR3ZwYzsNCiAgICAgICBzcGY9cGFzcyAoZ29vZ2xlLmNvbTogZG9tYWluIG9mIGZsb3djcnlwdC5jb21wYXRpYmlsaXR5QGdtYWlsLmNvbSBkZXNpZ25hdGVzIDIwOS44NS4yMjAuNDEgYXMgcGVybWl0dGVkIHNlbmRlcikgc210cC5tYWlsZnJvbT1mbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb207DQogICAgICAgZG1hcmM9cGFzcyAocD1OT05FIHNwPVFVQVJBTlRJTkUgZGlzPU5PTkUpIGhlYWRlci5mcm9tPWdtYWlsLmNvbQ0KREtJTS1TaWduYXR1cmU6IHY9MTsgYT1yc2Etc2hhMjU2OyBjPXJlbGF4ZWQvcmVsYXhlZDsNCiAgICAgICAgZD1nbWFpbC5jb207IHM9MjAyMTAxMTI7DQogICAgICAgIGg9dG86c3ViamVjdDptZXNzYWdlLWlkOmRhdGU6ZnJvbTptaW1lLXZlcnNpb246ZnJvbTp0bzpjYzpzdWJqZWN0DQogICAgICAgICA6ZGF0ZTptZXNzYWdlLWlkOnJlcGx5LXRvOw0KICAgICAgICBiaD1IY2hsVTNoQTl1RTRNcGtZbjRURGhuYjlFZmpzMEFpbmFET0xuRHphODZFPTsNCiAgICAgICAgYj1Hbm1mR3ZwY212Z1JmMTkxZFBvV1RSSW9HQkp6WkJUR3R2ZjJ5cUk2L0xLYWZpcjhyOGtsa2ZzWThvNjdUVTZobUUNCiAgICAgICAgIDJyeUIrV1BLWnV0Z3UxbGNXcUkxMGNBVnNIcXFkU3QvdTR6MFB0QmlsY24xS1BGbHZ1SGJRaXpBR2NJZG5YUE9aUkFZDQogICAgICAgICA4Q2pPdEZqOHorRUZhbnl3NkRBaWJSeFVzcnJGUU1LMlVGaTc0VU1udzY4MzBFcEpXZ1ZaVld4VHYxUEN4QThpZTRvZA0KICAgICAgICAgTG82SlhKY1ZodGx2TW5uNlV4QWNVUXRnZjVZbWpVVFdZQXVkSUVrTkE4UEE4TEI4TURVRmhzNDIzbDZUK1hjS1VlNEINCiAgICAgICAgIEg4K05GUmJDZXJPN3hVeVRZd25YeS94cDVDdDRoc2hVT3ZNalhlblJFRTZzKzd6MndUTE5FWUFTYzU3b2xoV2pSU2g3DQogICAgICAgICBNOU9RPT0NClgtR29vZ2xlLURLSU0tU2lnbmF0dXJlOiB2PTE7IGE9cnNhLXNoYTI1NjsgYz1yZWxheGVkL3JlbGF4ZWQ7DQogICAgICAgIGQ9MWUxMDAubmV0OyBzPTIwMjEwMTEyOw0KICAgICAgICBoPXRvOnN1YmplY3Q6bWVzc2FnZS1pZDpkYXRlOmZyb206bWltZS12ZXJzaW9uOngtZ20tbWVzc2FnZS1zdGF0ZQ0KICAgICAgICAgOmZyb206dG86Y2M6c3ViamVjdDpkYXRlOm1lc3NhZ2UtaWQ6cmVwbHktdG87DQogICAgICAgIGJoPUhjaGxVM2hBOXVFNE1wa1luNFREaG5iOUVmanMwQWluYURPTG5EemE4NkU9Ow0KICAgICAgICBiPTA0YTUwdHpmcFE2aDhCVlYyVHAzNk1aSFBMQzNvbXRMUWZVL1dkV2tuVUVNY2dLZ3V4R0h2K3grVTVCYTNmOGRLSA0KICAgICAgICAga2hOUTJ1NTJDWGs2WjRsSGdxTTJZaS91blpTem4wMi9Kek10K25Da3VlUzNjeitjeURhektsdWpPSC9pRHVoVVpsYTUNCiAgICAgICAgIEhOR2s1MDhPSk14aldVR3dnMGFseVd5NnRzVGpXb3NxbXNoRk9PSG1xRWNOSlJrRnJoYTBmTlVoVHlQanhwdmg5aWx4DQogICAgICAgICB2QXhjQk5IQjFOKzg0L2p2UU52ajJTS2NuL201R2l4Vk9TNkdjVlFUYllPWlhwd1JxOTFYRXlhVlk4L01sOCtVZ0lDUA0KICAgICAgICAgSU5VNnJmOU5tSmExTzFJREF4UzRobUtiWWRZMmVlSUVjNFp1dnB0MEVheU4yYWFJellsUXJiYzhQYWUwYWF6S1IxRVANCiAgICAgICAgIFBSR1E9PQ0KWC1HbS1NZXNzYWdlLVN0YXRlOiBBTm9CNXBsd3k2WEk3amRvaDh0aEhpeU51VklBQlVraHJsVmc0eWRIaXdvTmIzbWkweFp3WDhmZA0KCXVSTXRlbTc0VVZYbE5RUkU2djI2bzJtcmUxRXp5L1V1RWNEMWJnbkF2dDZsU3h4MFpnPT0NClgtR29vZ2xlLVNtdHAtU291cmNlOiBBQTBtcWY2V001bGs5cDFvek4rdlltRnRSZlliVnROT05CMFBsNkZaS0Y2WnJ1YkpFQzk5WGVSY0NIVmdQdzNZakk1U2tIWmhRcTdSSms1dW1FTkU4QXRqckk0PQ0KWC1SZWNlaXZlZDogYnkgMjAwMjphMDU6NmEwMDoyMzk2OmIwOjU3Mjo2OThiOjVmYTkgd2l0aCBTTVRQIGlkDQogZjIyLTIwMDIwYTA1NmEwMDIzOTYwMGIwMDU3MjY5OGI1ZmE5bXI4Nzg3NzIycGZjLjI4LjE2NjkyMDY0NDA0NTk7IFdlZCwgMjMNCiBOb3YgMjAyMiAwNDoyNzoyMCAtMDgwMCAoUFNUKQ0KTUlNRS1WZXJzaW9uOiAxLjANCkZyb206IEZsb3dDcnlwdCBDb21wYXRpYmlsaXR5IDxmbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb20-DQpEYXRlOiBXZWQsIDIzIE5vdiAyMDIyIDA4OjI3OjA4IC0wNDAwDQpNZXNzYWdlLUlEOiA8Q0FLYnVMVHBOKzZicF9fMEJtdzlZb1J1RUxkT3BPNm0wOGJuZkFIV0t5V2M1ME1EcjVRQG1haWwuZ21haWwuY29tPg0KU3ViamVjdDogZW5jcnlwdGVkIHRleHQgaW5zaWRlICJtZXNzYWdlIiBhdHRhY2htZW50DQpUbzogY2kudGVzdHMuZ21haWxAZmxvd2NyeXB0LmRldg0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PSIwMDAwMDAwMDAwMDBmMTVlMDgwNWVlMjI2ODY2Ig0KDQotLTAwMDAwMDAwMDAwMGYxNWUwODA1ZWUyMjY4NjYNCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L2FsdGVybmF0aXZlOyBib3VuZGFyeT0iMDAwMDAwMDAwMDAwZjE1ZTA3MDVlZTIyNjg2NCINCg0KLS0wMDAwMDAwMDAwMDBmMTVlMDcwNWVlMjI2ODY0DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9IlVURi04Ig0KDQoNCg0KLS0wMDAwMDAwMDAwMDBmMTVlMDcwNWVlMjI2ODY0DQpDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD0iVVRGLTgiDQoNCjxkaXYgZGlyPSJsdHIiPjxicj48L2Rpdj4NCg0KLS0wMDAwMDAwMDAwMDBmMTVlMDcwNWVlMjI2ODY0LS0NCi0tMDAwMDAwMDAwMDAwZjE1ZTA4MDVlZTIyNjg2Ng0KQ29udGVudC1UeXBlOiBhcHBsaWNhdGlvbi9vY3RldC1zdHJlYW07IG5hbWU9bWVzc2FnZQ0KQ29udGVudC1EaXNwb3NpdGlvbjogYXR0YWNobWVudDsgZmlsZW5hbWU9bWVzc2FnZQ0KQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0DQpDb250ZW50LUlEOiA8Zl9sYXRtZWszYzA-DQpYLUF0dGFjaG1lbnQtSWQ6IGZfbGF0bWVrM2MwDQoNCkxTMHRMUzFDUlVkSlRpQlFSMUFnVFVWVFUwRkhSUzB0TFMwdENsWmxjbk5wYjI0NklFWnNiM2REY25sd2RDQTFMakF1TkNCSGJXRnANCmJDQkZibU55ZVhCMGFXOXVJR1pzYjNkamNubHdkQzVqYjIwS1EyOXRiV1Z1ZERvZ1UyVmhiV3hsYzNOc2VTQnpaVzVrTENCeVpXTmwNCmFYWmxJR0Z1WkNCelpXRnlZMmdnWlc1amNubHdkR1ZrSUdWdFlXbHNDZ3AzWTBaTlFTdEJSSFl2TlhZMFVtZExRVkV2T0VOMloyeEsNCmR6TlRlVEIxVGtwa01HaE5jVmgzUkhCdGIzZzJielJ3WnlzMU5XOHhPRXBHT1hNS2VUZFBWRk5hY1VWdllVdFhiRVJMWW05bU4zbGsNClZqaE1TVkY0VjJneU5UQkNSazh3Vnk5SlExZHlTbE5rT0ZFcmNFMHpkR3RaY1dFeVZWcFpDa1oxVW1wR1owUk9lVVp2VDJjMFRHRmENCk9WSkdielJuU0RJclIxa3JMMFY2YTFOek5HRkJkbWxZUXpSdE5WWnZhRUZuVm5sTVRFWlZlV3BMTmdwc1JTOHdTV2x4ZWxKU1prTXYNCk1FOW1NR0pQU21KNlF6Um1LelpDTW1Jd1NuRlpUak5UT0VSRk1FYzFOVmxIV0RGRmJqRXZhMGhQWTBsUVdsZ0tla3hMYTJOQlpXWk8NCk9XdHFZVFF4YTJKa2FVSkRVMjQ0VjBoVUsxSkVhbGN4YzJSNFdDczRiVEpTVWk5RldERm9UMjV1ZW00eFNsTkdabVZ5Q21SMk0zRm8NClNFbHZhV3hxWlhoM1kwWlJPRUZyUWxkUFprUlRNMlIyYW5oQ2EyNHpTalk0YmxOcVpHczBhekE0VkVSQ05tUjZiMWhpWkZwa1dBb3kNCmVVTXpNV28zWkRJM1lXMVpMMmx2TTB4MlptOVRUek5JUkdWVU1EWm1UMHhrUkROWGFXaFRSWE5yYlZOU2QwY3ZhV0Y2VW1aQ1owOVMNClRsRUtOVkp3YTFJMVNHczJiRTVxVmxwMVpuVTNiWFZKTTJWRVRqTnZPSHB3U1VkbVFVaDVOMDFWY0ZsYU5rRTNWekpSTm04eVJrRngNCmNteHNRVnB3Q25OQlIyZzFWVUozWkVSc09UaHliREJKT1Zad1VtWm5kVlpXUjB4eFl6SndWM296VWxJemFFZExaemhRUkU5Wk1UVTENCmFuSnpZVzAxUWxONlpBcDZWRmcwTmpkM1VYb3hXRmMxTWxaMGVsbFhkM0l3WVRWWlNVdGtSelJSZG10S09WYzVPVTVJTWxRM2NsazANCk5DdEZOSEIzZUhKVlEyTXpjVU1LZUVWalYwOXNZaXRSYkdFdkszazVlVWN6TkV0R2JDdEtRa1pYUTFoUFVIbDJabVZ4ZVRWbU5VZGoNCmREbDVhbXR5VWxZNFlVSjNSRmREUVRodkNsQmthakpuYVhGa05HNU5hakkwWTI5TldpOW1TVGRXYkcwclduVTJNRGh4VWpWVk1HSkUNCmIzbERXRGRDZDFWM1JGTXhiM1l2VDFsMGJGRkZRZ3BGUVVOQlNrNTJiSEZLVFhFeE5ERnRiREI0VHpCdWJFYzJVaXRsU3poTFFuQTQNCmVtbGxXVmhXV25kcGJVZFhabmhxZVRoRFYweHlhbUpJY2pRS01XaDVTa0ozVFdsaVYwaFJSVnBqYUVKT00xVlhLM0FyWWpOSFRGVk8NCmFEQnJPVXNyWW5Sck9HZDFkRlZuVFVrMmFUZHBPRTVHYVdWVmNETm5DbTl3WmsxVU5uSTNVRk50ZUdOMllrOWxSbWxPYm1jM09FeEwNCmFXVXhSVFZ0Tm14bU1DOXpZV2hDV1V0dlJtSkZiR2hDUkZsalduWjRTR0ZNWkFwV05TdE5NRlk0VG1KTE5URk1iVmxJZUVjNVNGRkYNCldsTnllV0pIV0RsamQydzJUalpMV0dSRmNHNUpZVGhLV0dkSFQwTXJka1ZGUzBKdE5UY0tRM2RXVmtkUGIzRXZZakJ3U1dSNVJqbEcNClJHTTVUMWh4YkVWVE4yMUJaM000Y1VST1psZDVNMXBKYkdoRmFrNUZabEl3UzJkaFQyeExheTgyQ21zMWVUTkxibWhoYjB0aFNsVTUNClVWWTRTV2hNVkZwcmNrTXpObnBSZEZsSmRFTk9jREZZZVU5dFltOU1UbkJEVUVvNE5qQkZhM1ZJYW5od1V3bzNNbkYwU2pGb2NWSTANCmNsazJlbVkzWTNGc1VrdDVTa05YUm1OSUx6SnlPRzlvU1RaUVNGTktOR1ZXY2taQlVXbGpMMlJEV0hKV1ZrVlVTRThLVm5FM1pVSnINCkx6RlRWMkZMU2xsVVZTdFdhekYwUzBSS1VscFlWMlpMVURoclNFZHRPRzk0Ym1kM2FtTlBWMlpCUldselVVRmllV0owY1c1WkNrTjQNCldDOW5LMlZGTUZGbFZqQk5WREF6UW5CTmJXRlRTMjlJT0ZZdlltZFRUVFJKTVRrelRIUjJVV3B1UzFoVmIzTkxNbmxuUlUwME1qTTMNClNRb3pkMGxrVW01elJrUTViR0V6UjBkb1ptWktUVWQwWWxOSlpGb3piMHRWYm1WeWNYcERkbE4zZWxNMUswSjNURzR4UTBRd1dVTjYNCmJIVk9UalFLYjBGMloycEhTVmt5TmtsR2FFOUVPSGh4UkVVelpGWnZkVWxZU1ZwdE1rbHVVRWcwY1VOdlFsVkVZakowTjNkWVNqVk0NCldFUjJPVmQzTHpJNUNsTnpabWRIYVM5T1J6VnRWMUJRYVhad1FtSnVkbUpOTVU1MFN6QkJXREY2WTJKVFpHTmtVMU5KTmprdmNVZEMNClprWTVWV3RNU0hSSFpUWjRaUXByVlZZNWFWcGhlalI2TlZwRkswNTBWMEZVZDFFdmFHNDJlVkY0V0RCRU1XRlVheTlKTHpOemRDdFENClUyNU9WakpNVDNSSE1FUXlSR0oyV1VZS01uTk9aVVJ1Y2l0aFNYUkhXa2ROTkRoNmRITmhkblUxWVRONFRVeDRaeXRFVUc5MVJsSnQNCmFpdHVTRWhDY0ZGbE5EWXpSRUZJWVVScU0wbElDbUpoUzFsV1VXUlhabGxFV1VKVmMwOWxRbUY2WjFoTWFUaG5WVzlQZUU0MmF6a3INClJXTlFlVWgxY0hrNFMxVlpSRTV1YVVkdlkyeElTR3d6WXdwQmRXNVRVWHB5TWxCeVUyMUtLMnR5ZUhkT1pGVmphbXhQWmtwd0NqMVoNClZESkJDaTB0TFMwdFJVNUVJRkJIVUNCTlJWTlRRVWRGTFMwdExTMD0NCi0tMDAwMDAwMDAwMDAwZjE1ZTA4MDVlZTIyNjg2Ni0tDQo=", + "historyId": "2420926", + "internalDate": "1669206428000" + } +} \ No newline at end of file diff --git a/test/source/mock/google/exported-messages/message-export-184a87a7b32dd009.json b/test/source/mock/google/exported-messages/message-export-184a87a7b32dd009.json new file mode 100644 index 00000000000..f8d53854ffe --- /dev/null +++ b/test/source/mock/google/exported-messages/message-export-184a87a7b32dd009.json @@ -0,0 +1,153 @@ +{ + "acctEmail": "ci.tests.gmail@flowcrypt.test", + "full": { + "id": "184a87a7b32dd009", + "threadId": "184a87a7b32dd009", + "labelIds": [ + "IMPORTANT", + "CATEGORY_PERSONAL", + "INBOX" + ], + "snippet": "Plain message", + "payload": { + "partId": "", + "mimeType": "multipart/mixed", + "filename": "", + "headers": [ + { + "name": "X-Gm-Message-State", + "value": "ANoB5pkz8Ib9pqSD/x3au0unFaDyBYh0kP+CVoe72ZzT8HYaJq7OfTOh wMLtr2zVFIPcDjC+fuqdaFuKTWzdK59x+CCl1XYzQFQ+QNrQDw==" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "From", + "value": "sender@domain.com" + }, + { + "name": "Date", + "value": "Thu, 24 Nov 2022 03:11:36 -0400" + }, + { + "name": "Subject", + "value": "Message attachment with plain text" + }, + { + "name": "To", + "value": "flowcrypt.compatibility@gmail.com" + }, + { + "name": "Content-Type", + "value": "multipart/mixed; boundary=\"00000000000062238205ee321e74\"" + } + ], + "body": { + "size": 0 + }, + "parts": [ + { + "partId": "0", + "mimeType": "multipart/alternative", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "multipart/alternative; boundary=\"00000000000062238005ee321e72\"" + } + ], + "body": { + "size": 0 + }, + "parts": [ + { + "partId": "0.0", + "mimeType": "text/plain", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "text/plain; charset=\"UTF-8\"" + } + ], + "body": { + "size": 15, + "data": "UGxhaW4gbWVzc2FnZQ0K" + } + }, + { + "partId": "0.1", + "mimeType": "text/html", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "text/html; charset=\"UTF-8\"" + } + ], + "body": { + "size": 36, + "data": "PGRpdiBkaXI9Imx0ciI-UGxhaW4gbWVzc2FnZTwvZGl2Pg0K" + } + } + ] + }, + { + "partId": "1", + "mimeType": "application/octet-stream", + "filename": "message", + "headers": [ + { + "name": "Content-Type", + "value": "application/octet-stream; name=message" + }, + { + "name": "Content-Disposition", + "value": "attachment; filename=message" + }, + { + "name": "Content-Transfer-Encoding", + "value": "base64" + }, + { + "name": "Content-ID", + "value": "" + }, + { + "name": "X-Attachment-Id", + "value": "f_lauqkou20" + } + ], + "body": { + "attachmentId": "ANGjdJ-3JOTnvlgV3ofBhgP1QpJiBpKhZ2ciP7JVzMSnZqPtT5s3OVRk7s1zWz0YELeOihyLaI2KT5-OLMPmM-DOo_LDpI4LDlmhStGBxPViSBUp8TjkxAZJKLRNmwrtd_SDvfWWCHDKWIcw3vXDioudadcoYflqJwK28PebggOnI7Xo7YHbDs7F3kvQfuQH2s29Mf48QwIy-bQ9mbqbgZvqG6K8OhWpb9amWMy_IIZ-EAU5TTS4p1ROo3Sb6imfd-esxz0ZM7GRXVW_edBSwQ7GhD3g83GxE0jS9fUGh7xwN71NgWjti3SgeKJrIoCA0g_yL0m0c3fOq7FpS-FtEES9gYIbBlC75r9Si-LMqTKCV7FbiyM0Mx_9RfVytg-4BMUMkEe3WfPkvxiw1Egi", + "size": 1853 + } + } + ] + }, + "sizeEstimate": 7725, + "historyId": "2422243", + "internalDate": "1669273896000" + }, + "attachments": { + "ANGjdJ-3JOTnvlgV3ofBhgP1QpJiBpKhZ2ciP7JVzMSnZqPtT5s3OVRk7s1zWz0YELeOihyLaI2KT5-OLMPmM-DOo_LDpI4LDlmhStGBxPViSBUp8TjkxAZJKLRNmwrtd_SDvfWWCHDKWIcw3vXDioudadcoYflqJwK28PebggOnI7Xo7YHbDs7F3kvQfuQH2s29Mf48QwIy-bQ9mbqbgZvqG6K8OhWpb9amWMy_IIZ-EAU5TTS4p1ROo3Sb6imfd-esxz0ZM7GRXVW_edBSwQ7GhD3g83GxE0jS9fUGh7xwN71NgWjti3SgeKJrIoCA0g_yL0m0c3fOq7FpS-FtEES9gYIbBlC75r9Si-LMqTKCV7FbiyM0Mx_9RfVytg-4BMUMkEe3WfPkvxiw1Egi": { + "data": "LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEZsb3dDcnlwdCA1LjAuNCBHbWFpbCBFbmNyeXB0aW9uIGZsb3djcnlwdC5jb20KQ29tbWVudDogU2VhbWxlc3NseSBzZW5kLCByZWNlaXZlIGFuZCBzZWFyY2ggZW5jcnlwdGVkIGVtYWlsCgp3Y0ZNQStBRHYvNXY0UmdLQVEvOEN2Z2xKdzNTeTB1TkpkMGhNcVh3RHBtb3g2bzRwZys1NW8xOEpGOXMKeTdPVFNacUVvYUtXbERLYm9mN3lkVjhMSVF4V2gyNTBCRk8wVy9JQ1dySlNkOFErcE0zdGtZcWEyVVpZCkZ1UmpGZ0ROeUZvT2c0TGFaOVJGbzRnSDIrR1krL0V6a1NzNGFBdmlYQzRtNVZvaEFnVnlMTEZVeWpLNgpsRS8wSWlxelJSZkMvME9mMGJPSmJ6QzRmKzZCMmIwSnFZTjNTOERFMEc1NVlHWDFFbjEva0hPY0lQWlgKekxLa2NBZWZOOWtqYTQxa2JkaUJDU244V0hUK1JEalcxc2R4WCs4bTJSUi9FWDFoT25uem4xSlNGZmVyCmR2M3FoSElvaWxqZXh3Y0ZROEFrQldPZkRTM2R2anhCa24zSjY4blNqZGs0azA4VERCNmR6b1hiZFpkWAoyeUMzMWo3ZDI3YW1ZL2lvM0x2Zm9TTzNIRGVUMDZmT0xkRDNXaWhTRXNrbVNSd0cvaWF6UmZCZ09STlEKNVJwa1I1SGs2bE5qVlp1ZnU3bXVJM2VETjNvOHpwSUdmQUh5N01VcFlaNkE3VzJRNm8yRkFxcmxsQVpwCnNBR2g1VUJ3ZERsOThybDBJOVZwUmZndVZWR0xxYzJwV3ozUlIzaEdLZzhQRE9ZMTU1anJzYW01QlN6ZAp6VFg0Njd3UXoxWFc1MlZ0ellXd3IwYTVZSUtkRzRRdmtKOVc5OU5IMlQ3clk0NCtFNHB3eHJVQ2MzcUMKeEVjV09sYitRbGEvK3k5eUczNEtGbCtKQkZXQ1hPUHl2ZmVxeTVmNUdjdDl5amtyUlY4YUJ3RFdDQThvClBkajJnaXFkNG5NajI0Y29NWi9mSTdWbG0rWnU2MDhxUjVVMGJEb3lDWDdCd1V3RFMxb3YvT1l0bFFFQgpFQUNBSk52bHFKTXExNDFtbDB4TzBubEc2UitlSzhLQnA4emllWVhWWndpbUdXZnhqeThDV0xyamJIcjQKMWh5SkJ3TWliV0hRRVpjaEJOM1VXK3ArYjNHTFVOaDBrOUsrYnRrOGd1dFVnTUk2aTdpOE5GaWVVcDNnCm9wZk1UNnI3UFNteGN2Yk9lRmlObmc3OExLaWUxRTVtNmxmMC9zYWhCWUtvRmJFbGhCRFljWnZ4SGFMZApWNStNMFY4TmJLNTFMbVlIeEc5SFFFWlNyeWJHWDljd2w2TjZLWGRFcG5JYThKWGdHT0MrdkVFS0JtNTcKQ3dWVkdPb3EvYjBwSWR5RjlGRGM5T1hxbEVTN21BZ3M4cUROZld5M1pJbGhFak5FZlIwS2dhT2xLay82Cms1eTNLbmhhb0thSlU5UVY4SWhMVFprckMzNnpRdFlJdENOcDFYeU9tYm9MTnBDUEo4NjBFa3VIanhwUwo3MnF0SjFocVI0clk2emY3Y3FsUkt5SkNXRmNILzJyOG9oSTZQSFNKNGVWckZBUWljL2RDWHJWVkVUSE8KVnE3ZUJrLzFTV2FLSllUVStWazF0S0RKUlpYV2ZLUDhrSEdtOG94bmd3amNPV2ZBRWlzUUFieWJ0cW5ZCkN4WC9nK2VFMFFlVjBNVDAzQnBNbWFTS29IOFYvYmdTTTRJMTkzTHR2UWpuS1hVb3NLMnlnRU00MjM3SQozd0lkUm5zRkQ5bGEzR0doZmZKTUd0YlNJZFozb0tVbmVycXpDdlN3elM1K0J3TG4xQ0QwWUN6bHVOTjQKb0F2Z2pHSVkyNklGaE9EOHhxREUzZFZvdUlYSVptMkluUEg0cUNvQlVEYjJ0N3dYSjVMWER2OVd3LzI5ClNzZmdHaS9ORzVtV1BQaXZwQmJudmJNMU50SzBBWDF6Y2JTZGNkU1NJNjkvcUdCZkY5VWtMSHRHZTZ4ZQprVVY5aVphejR6NVpFK050V0FUd1EvaG42eVF4WDBEMWFUay9JLzNzdCtQU25OVjJMT3RHMEQyRGJ2WUYKMnNOZURucithSXRHWkdNNDh6dHNhdnU1YTN4TUx4ZytEUG91RlJtaituSEhCcFFlNDYzREFIYURqM0lICmJhS1lWUWRXZllEWUJVc09lQmF6Z1hMaThnVW9PeE42azkrRWNQeUh1cHk4S1VZRE5uaUdvY2xISGwzYwpBdW5TUXpyMlByU21KK2tyeHdOZFVjamxPZkpwCj1ZVDJBCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0", + "size": 1853 + } + }, + "raw": { + "id": "184a87a7b32dd009", + "threadId": "184a87a7b32dd009", + "labelIds": [ + "IMPORTANT", + "CATEGORY_PERSONAL", + "INBOX" + ], + "snippet": "Plain message", + "sizeEstimate": 7725, + "raw": "RGVsaXZlcmVkLVRvOiBjaS50ZXN0cy5nbWFpbEBmbG93Y3J5cHQuZGV2DQpSZWNlaXZlZDogYnkgMjAwMjphMDU6NzEwODo2ODgxOjA6MDowOjAgd2l0aCBTTVRQIGlkIG0xY3NwMzUwMTMyMGdkZDsNCiAgICAgICAgV2VkLCAyMyBOb3YgMjAyMiAyMzoxMTo1MCAtMDgwMCAoUFNUKQ0KWC1SZWNlaXZlZDogYnkgMjAwMjphMDU6NjUxMjoxNjk4OmIwOjRhMjo0YjQzOjlhYWQgd2l0aCBTTVRQIGlkIGJ1MjQtMjAwMjBhMDU2NTEyMTY5ODAwYjAwNGEyNGI0MzlhYWRtcjExNTQyNjY0bGZiLjIxMy4xNjY5MjczOTA5OTIyOw0KICAgICAgICBXZWQsIDIzIE5vdiAyMDIyIDIzOjExOjQ5IC0wODAwIChQU1QpDQpBUkMtU2VhbDogaT0xOyBhPXJzYS1zaGEyNTY7IHQ9MTY2OTI3MzkwOTsgY3Y9bm9uZTsNCiAgICAgICAgZD1nb29nbGUuY29tOyBzPWFyYy0yMDE2MDgxNjsNCiAgICAgICAgYj1URlBNUk5BdTQ5Ukh3Rm5IOWpMOVY0dWkrOFN0TUNiR29pYmhmN0Z0Nk94cFhGbmpsYVE0cXFpbXlnZFhZREpWRFkNCiAgICAgICAgIFhaSmtFMEQ4bVpNNnpXZFhNQjlHSllQamsxMUF2MmdMYUlHUEllNVRSMTNTNk1KeVErSFQvcCtQVEVvSVdKNVJvNjMxDQogICAgICAgICBNMkFXVTdYMFRnd1M4emFoWGpXRmpVVWVvQy83SHlEd2ZVTWJNWkttOGtaa3dVOEtBbk9UK2w1clBxYzNaUWg2Umplcw0KICAgICAgICAgVEdBSnMrODVSY3FDd2NsSUVvcGhRbFNqeS81c3kxNTBybjZ3czZFNUVQV01GdFRZczM3VGJ4eVVFdnFJVE9vSG45VlMNCiAgICAgICAgIHJ1YTZlVW1ObENhdkkvWVAwcTFNSmV3bkVvVUErWmppamVIUnhXeTJDT3hWcHl1OWRJNmlyMUNBc3Y2cmxOTEpxdVRaDQogICAgICAgICBzMlZnPT0NCkFSQy1NZXNzYWdlLVNpZ25hdHVyZTogaT0xOyBhPXJzYS1zaGEyNTY7IGM9cmVsYXhlZC9yZWxheGVkOyBkPWdvb2dsZS5jb207IHM9YXJjLTIwMTYwODE2Ow0KICAgICAgICBoPXRvOnN1YmplY3Q6bWVzc2FnZS1pZDpkYXRlOmZyb206bWltZS12ZXJzaW9uOmRraW0tc2lnbmF0dXJlOw0KICAgICAgICBiaD1GL1hsZ1BITmJyVkZ4RE9DWmI5enB3blNFcys4UnhtK3BZMk1icG1GTFlZPTsNCiAgICAgICAgYj1nc0ZXOHArd2kwaHZJWlNudlJZZG1rYTE3dVFpTlg4MUNuZEpsU2M1cmozT2dJajROS0VOM2hQS1QrdUhVOEpaTXYNCiAgICAgICAgIDlDM3NvT29HSFN5bDdsT0d0RlVBbFk0dURvak1NTTBHbnJKL0pKTjVLeTV1SS9oWTIxWjNQYklEQUJRTTNjMWIxY1hJDQogICAgICAgICBqcVgyVDV3RkJqMmhhSHgxSHpKQ0xGMWY3MHBIOEEyaFFOWkk1c1dTcjFWN0VyZE1vRHNOZzl5N3NUNDhSZVRSRnVpUQ0KICAgICAgICAgd0o1b0QwSldtNmlmTTZ0eXFFaGNvcVo3NGdqL1JHUXBUTnhic1hYaEEyVDB5R1M4MlN6MWhjY0lwMVJWbDFWdTgvdXkNCiAgICAgICAgIDZMUW4rN2x4YnVUUjJod29MSVhGWjJqZmhhSlZNeWMrSmxpQXRMRjg0MWhOMmM5SU9rWldDQVovTUMxajNud2tGRXlCDQogICAgICAgICA4emNnPT0NCkFSQy1BdXRoZW50aWNhdGlvbi1SZXN1bHRzOiBpPTE7IG14Lmdvb2dsZS5jb207DQogICAgICAgZGtpbT1wYXNzIGhlYWRlci5pPUBmbG93Y3J5cHQuY29tIGhlYWRlci5zPWdvb2dsZSBoZWFkZXIuYj1neDVqMlJodTsNCiAgICAgICBzcGY9cGFzcyAoZ29vZ2xlLmNvbTogZG9tYWluIG9mIGlvYW5AZmxvd2NyeXB0LmNvbSBkZXNpZ25hdGVzIDIwOS44NS4yMjAuNDEgYXMgcGVybWl0dGVkIHNlbmRlcikgc210cC5tYWlsZnJvbT1pb2FuQGZsb3djcnlwdC5jb207DQogICAgICAgZG1hcmM9cGFzcyAocD1SRUpFQ1Qgc3A9UkVKRUNUIGRpcz1OT05FKSBoZWFkZXIuZnJvbT1mbG93Y3J5cHQuY29tDQpSZXR1cm4tUGF0aDogPGlvYW5AZmxvd2NyeXB0LmNvbT4NClJlY2VpdmVkOiBmcm9tIG1haWwtc29yLWY0MS5nb29nbGUuY29tIChtYWlsLXNvci1mNDEuZ29vZ2xlLmNvbS4gWzIwOS44NS4yMjAuNDFdKQ0KICAgICAgICBieSBteC5nb29nbGUuY29tIHdpdGggU01UUFMgaWQgYTIxLTIwMDIwYTE5NGY1NTAwMDAwMGIwMDQ5ZjUzZjJlODhmc29yNjQyMjVsZmsuMTQuMjAyMi4xMS4yMy4yMy4xMS40OQ0KICAgICAgICBmb3IgPGNpLnRlc3RzLmdtYWlsQGZsb3djcnlwdC5kZXY-DQogICAgICAgIChHb29nbGUgVHJhbnNwb3J0IFNlY3VyaXR5KTsNCiAgICAgICAgV2VkLCAyMyBOb3YgMjAyMiAyMzoxMTo0OSAtMDgwMCAoUFNUKQ0KUmVjZWl2ZWQtU1BGOiBwYXNzIChnb29nbGUuY29tOiBkb21haW4gb2YgaW9hbkBmbG93Y3J5cHQuY29tIGRlc2lnbmF0ZXMgMjA5Ljg1LjIyMC40MSBhcyBwZXJtaXR0ZWQgc2VuZGVyKSBjbGllbnQtaXA9MjA5Ljg1LjIyMC40MTsNCkF1dGhlbnRpY2F0aW9uLVJlc3VsdHM6IG14Lmdvb2dsZS5jb207DQogICAgICAgZGtpbT1wYXNzIGhlYWRlci5pPUBmbG93Y3J5cHQuY29tIGhlYWRlci5zPWdvb2dsZSBoZWFkZXIuYj1neDVqMlJodTsNCiAgICAgICBzcGY9cGFzcyAoZ29vZ2xlLmNvbTogZG9tYWluIG9mIGlvYW5AZmxvd2NyeXB0LmNvbSBkZXNpZ25hdGVzIDIwOS44NS4yMjAuNDEgYXMgcGVybWl0dGVkIHNlbmRlcikgc210cC5tYWlsZnJvbT1pb2FuQGZsb3djcnlwdC5jb207DQogICAgICAgZG1hcmM9cGFzcyAocD1SRUpFQ1Qgc3A9UkVKRUNUIGRpcz1OT05FKSBoZWFkZXIuZnJvbT1mbG93Y3J5cHQuY29tDQpES0lNLVNpZ25hdHVyZTogdj0xOyBhPXJzYS1zaGEyNTY7IGM9cmVsYXhlZC9yZWxheGVkOw0KICAgICAgICBkPWZsb3djcnlwdC5jb207IHM9Z29vZ2xlOw0KICAgICAgICBoPXRvOnN1YmplY3Q6bWVzc2FnZS1pZDpkYXRlOmZyb206bWltZS12ZXJzaW9uOmZyb206dG86Y2M6c3ViamVjdA0KICAgICAgICAgOmRhdGU6bWVzc2FnZS1pZDpyZXBseS10bzsNCiAgICAgICAgYmg9Ri9YbGdQSE5iclZGeERPQ1piOXpwd25TRXMrOFJ4bStwWTJNYnBtRkxZWT07DQogICAgICAgIGI9Z3g1ajJSaHVjZkE1dnRmT0lUOGF5MGZnR3J1V3I3cG5TMzZXalJuK3B5c0NVaEtObWo5cVlOSmVDRVlMMGpydWdyDQogICAgICAgICB6dWhFN1doY0tQcnNkMTJreDhHV0tDZkFzVFkyTjhXclNmbXBKdE51ekVxSkJtVDFCVEs4blFnbFF6RjFKY2t4RGJpSw0KICAgICAgICAgUCtrVDJVS2ttTnFQTzJCL28yNHV2MDl6NUFxeThWTmpZaDErOD0NClgtR29vZ2xlLURLSU0tU2lnbmF0dXJlOiB2PTE7IGE9cnNhLXNoYTI1NjsgYz1yZWxheGVkL3JlbGF4ZWQ7DQogICAgICAgIGQ9MWUxMDAubmV0OyBzPTIwMjEwMTEyOw0KICAgICAgICBoPXRvOnN1YmplY3Q6bWVzc2FnZS1pZDpkYXRlOmZyb206bWltZS12ZXJzaW9uOngtZ20tbWVzc2FnZS1zdGF0ZQ0KICAgICAgICAgOmZyb206dG86Y2M6c3ViamVjdDpkYXRlOm1lc3NhZ2UtaWQ6cmVwbHktdG87DQogICAgICAgIGJoPUYvWGxnUEhOYnJWRnhET0NaYjl6cHduU0VzKzhSeG0rcFkyTWJwbUZMWVk9Ow0KICAgICAgICBiPWZ5ZFd4UTBCdzNManMrUngvK2RoTlR4TVlZMjlabkxhUGdtd0laczlXVDBMdWx2c1RrWVIwMDV5QThqK1ArMy81Kw0KICAgICAgICAgK1B6dkxENmtRNDFMRWZSWkV2Y0ZLbTA5dG45ZnJ5WDM5Nzc2VHJIUVNST2tkbDFweU1IZkhkdlBoSVFpL2NRTkkrUisNCiAgICAgICAgIGpRblc1YUVHa1AreDR3dFpleVpUZjVZQ09NdTBQRXNEOHNjSlhVOU12N2JpMUVpVGdoQWJKRkQxbVlEZ3Q5WnR1Nk1zDQogICAgICAgICAxUnAvbnlXQWtXWlpxUDdwbHVSRFE5b0tiOENKL3N3SExBczhXaGNXWjJOcE41QytHOFVsL3Nuc05RbEEvTG1xQ2FIdw0KICAgICAgICAgbDNmOEM5c2MxTUNrcFZKMGZNSGhIdzlMMkU0c3hhWGVidkF5MDZMRjd1Vm5sNkVzUzlFZG9ZemdSaVRUb3VNQzMwc2sNCiAgICAgICAgIE1KRWc9PQ0KWC1HbS1NZXNzYWdlLVN0YXRlOiBBTm9CNXBrejhJYjlwcVNEL3gzYXUwdW5GYUR5QlloMGtQK0NWb2U3Mlp6VDhIWWFKcTdPZlRPaA0KCXdNTHRyMnpWRklQY0RqQytmdXFkYUZ1S1RXemRLNTl4K0NDbDFYWXpRRlErUU5yUUR3PT0NClgtR29vZ2xlLVNtdHAtU291cmNlOiBBQTBtcWY2RDVMQjRKQmRPTVBiSmtrc0lCZHhNTHBmSmFxWGd4QmJjay9xcXN3SFZ1dXQ4MVd1VU5DUk5PWjRTajR3YmNVMUs4bXV2MHo5N2FOUWgvRHRXYlF3PQ0KWC1SZWNlaXZlZDogYnkgMjAwMjphMDU6NjUxMjoyYzg1OmIwOjRhMjo1OTM3OmU5YiB3aXRoIFNNVFAgaWQNCiBkdzUtMjAwMjBhMDU2NTEyMmM4NTAwYjAwNGEyNTkzNzBlOWJtcjk5OTc2MTJsZmIuMTEuMTY2OTI3MzkwODkzOTsgV2VkLCAyMw0KIE5vdiAyMDIyIDIzOjExOjQ4IC0wODAwIChQU1QpDQpNSU1FLVZlcnNpb246IDEuMA0KRnJvbTogSW9hbiBhdCBGbG93Q3J5cHQgPGlvYW5AZmxvd2NyeXB0LmNvbT4NCkRhdGU6IFRodSwgMjQgTm92IDIwMjIgMDM6MTE6MzYgLTA0MDANCk1lc3NhZ2UtSUQ6IDxDQVBDM2toQWVWWjNGZFNLS0VCSGFxTFphRlpRRTNNOHJpNWZUREZvSm5obzljSlV1ckFAbWFpbC5nbWFpbC5jb20-DQpTdWJqZWN0OiBNZXNzYWdlIGF0dGFjaG1lbnQgd2l0aCBwbGFpbiB0ZXh0DQpUbzogY2kudGVzdHMuZ21haWxAZmxvd2NyeXB0LmRldg0KQ29udGVudC1UeXBlOiBtdWx0aXBhcnQvbWl4ZWQ7IGJvdW5kYXJ5PSIwMDAwMDAwMDAwMDA2MjIzODIwNWVlMzIxZTc0Ig0KDQotLTAwMDAwMDAwMDAwMDYyMjM4MjA1ZWUzMjFlNzQNCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L2FsdGVybmF0aXZlOyBib3VuZGFyeT0iMDAwMDAwMDAwMDAwNjIyMzgwMDVlZTMyMWU3MiINCg0KLS0wMDAwMDAwMDAwMDA2MjIzODAwNWVlMzIxZTcyDQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9IlVURi04Ig0KDQpQbGFpbiBtZXNzYWdlDQoNCi0tMDAwMDAwMDAwMDAwNjIyMzgwMDVlZTMyMWU3Mg0KQ29udGVudC1UeXBlOiB0ZXh0L2h0bWw7IGNoYXJzZXQ9IlVURi04Ig0KDQo8ZGl2IGRpcj0ibHRyIj5QbGFpbiBtZXNzYWdlPC9kaXY-DQoNCi0tMDAwMDAwMDAwMDAwNjIyMzgwMDVlZTMyMWU3Mi0tDQotLTAwMDAwMDAwMDAwMDYyMjM4MjA1ZWUzMjFlNzQNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtOyBuYW1lPW1lc3NhZ2UNCkNvbnRlbnQtRGlzcG9zaXRpb246IGF0dGFjaG1lbnQ7IGZpbGVuYW1lPW1lc3NhZ2UNCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NA0KQ29udGVudC1JRDogPGZfbGF1cWtvdTIwPg0KWC1BdHRhY2htZW50LUlkOiBmX2xhdXFrb3UyMA0KDQpMUzB0TFMxQ1JVZEpUaUJRUjFBZ1RVVlRVMEZIUlMwdExTMHRDbFpsY25OcGIyNDZJRVpzYjNkRGNubHdkQ0ExTGpBdU5DQkhiV0ZwDQpiQ0JGYm1OeWVYQjBhVzl1SUdac2IzZGpjbmx3ZEM1amIyMEtRMjl0YldWdWREb2dVMlZoYld4bGMzTnNlU0J6Wlc1a0xDQnlaV05sDQphWFpsSUdGdVpDQnpaV0Z5WTJnZ1pXNWpjbmx3ZEdWa0lHVnRZV2xzQ2dwM1kwWk5RU3RCUkhZdk5YWTBVbWRMUVZFdk9FTjJaMnhLDQpkek5UZVRCMVRrcGtNR2hOY1ZoM1JIQnRiM2cyYnpSd1p5czFOVzh4T0VwR09YTUtlVGRQVkZOYWNVVnZZVXRYYkVSTFltOW1OM2xrDQpWamhNU1ZGNFYyZ3lOVEJDUms4d1Z5OUpRMWR5U2xOa09GRXJjRTB6ZEd0WmNXRXlWVnBaQ2taMVVtcEdaMFJPZVVadlQyYzBUR0ZhDQpPVkpHYnpSblNESXJSMWtyTDBWNmExTnpOR0ZCZG1sWVF6UnROVlp2YUVGblZubE1URVpWZVdwTE5ncHNSUzh3U1dseGVsSlNaa012DQpNRTltTUdKUFNtSjZRelJtS3paQ01tSXdTbkZaVGpOVE9FUkZNRWMxTlZsSFdERkZiakV2YTBoUFkwbFFXbGdLZWt4TGEyTkJaV1pPDQpPV3RxWVRReGEySmthVUpEVTI0NFYwaFVLMUpFYWxjeGMyUjRXQ3M0YlRKU1VpOUZXREZvVDI1dWVtNHhTbE5HWm1WeUNtUjJNM0ZvDQpTRWx2YVd4cVpYaDNZMFpST0VGclFsZFBaa1JUTTJSMmFuaENhMjR6U2pZNGJsTnFaR3MwYXpBNFZFUkNObVI2YjFoaVpGcGtXQW95DQplVU16TVdvM1pESTNZVzFaTDJsdk0weDJabTlUVHpOSVJHVlVNRFptVDB4a1JETlhhV2hUUlhOcmJWTlNkMGN2YVdGNlVtWkNaMDlTDQpUbEVLTlZKd2ExSTFTR3MyYkU1cVZscDFablUzYlhWSk0yVkVUak52T0hwd1NVZG1RVWg1TjAxVmNGbGFOa0UzVnpKUk5tOHlSa0Z4DQpjbXhzUVZwd0NuTkJSMmcxVlVKM1pFUnNPVGh5YkRCSk9WWndVbVpuZFZaV1IweHhZekp3VjNvelVsSXphRWRMWnpoUVJFOVpNVFUxDQphbkp6WVcwMVFsTjZaQXA2VkZnME5qZDNVWG94V0ZjMU1sWjBlbGxYZDNJd1lUVlpTVXRrUnpSUmRtdEtPVmM1T1U1SU1sUTNjbGswDQpOQ3RGTkhCM2VISlZRMk16Y1VNS2VFVmpWMDlzWWl0UmJHRXZLM2s1ZVVjek5FdEdiQ3RLUWtaWFExaFBVSGwyWm1WeGVUVm1OVWRqDQpkRGw1YW10eVVsWTRZVUozUkZkRFFUaHZDbEJrYWpKbmFYRmtORzVOYWpJMFkyOU5XaTltU1RkV2JHMHJXblUyTURoeFVqVlZNR0pFDQpiM2xEV0RkQ2QxVjNSRk14YjNZdlQxbDBiRkZGUWdwRlFVTkJTazUyYkhGS1RYRXhOREZ0YkRCNFR6QnViRWMyVWl0bFN6aExRbkE0DQplbWxsV1ZoV1duZHBiVWRYWm5ocWVUaERWMHh5YW1KSWNqUUtNV2g1U2tKM1RXbGlWMGhSUlZwamFFSk9NMVZYSzNBcllqTkhURlZPDQphREJyT1VzclluUnJPR2QxZEZWblRVazJhVGRwT0U1R2FXVlZjRE5uQ205d1prMVVObkkzVUZOdGVHTjJZazlsUm1sT2JtYzNPRXhMDQphV1V4UlRWdE5teG1NQzl6WVdoQ1dVdHZSbUpGYkdoQ1JGbGpXblo0U0dGTVpBcFdOU3ROTUZZNFRtSkxOVEZNYlZsSWVFYzVTRkZGDQpXbE55ZVdKSFdEbGpkMncyVGpaTFdHUkZjRzVKWVRoS1dHZEhUME1yZGtWRlMwSnROVGNLUTNkV1ZrZFBiM0V2WWpCd1NXUjVSamxHDQpSR001VDFoeGJFVlROMjFCWjNNNGNVUk9abGQ1TTFwSmJHaEZhazVGWmxJd1MyZGhUMnhMYXk4MkNtczFlVE5MYm1oaGIwdGhTbFU1DQpVVlk0U1doTVZGcHJja016Tm5wUmRGbEpkRU5PY0RGWWVVOXRZbTlNVG5CRFVFbzROakJGYTNWSWFuaHdVd28zTW5GMFNqRm9jVkkwDQpjbGsyZW1ZM1kzRnNVa3Q1U2tOWFJtTklMekp5T0c5b1NUWlFTRk5LTkdWV2NrWkJVV2xqTDJSRFdISldWa1ZVU0U4S1ZuRTNaVUpyDQpMekZUVjJGTFNsbFVWU3RXYXpGMFMwUktVbHBZVjJaTFVEaHJTRWR0T0c5NGJtZDNhbU5QVjJaQlJXbHpVVUZpZVdKMGNXNVpDa040DQpXQzluSzJWRk1GRmxWakJOVkRBelFuQk5iV0ZUUzI5SU9GWXZZbWRUVFRSSk1Ua3pUSFIyVVdwdVMxaFZiM05MTW5sblJVMDBNak0zDQpTUW96ZDBsa1VtNXpSa1E1YkdFelIwZG9abVpLVFVkMFlsTkpaRm96YjB0VmJtVnljWHBEZGxOM2VsTTFLMEozVEc0eFEwUXdXVU42DQpiSFZPVGpRS2IwRjJaMnBIU1ZreU5rbEdhRTlFT0hoeFJFVXpaRlp2ZFVsWVNWcHRNa2x1VUVnMGNVTnZRbFZFWWpKME4zZFlTalZNDQpXRVIyT1ZkM0x6STVDbE56Wm1kSGFTOU9SelZ0VjFCUWFYWndRbUp1ZG1KTk1VNTBTekJCV0RGNlkySlRaR05rVTFOSk5qa3ZjVWRDDQpaa1k1Vld0TVNIUkhaVFo0WlFwclZWWTVhVnBoZWpSNk5WcEZLMDUwVjBGVWQxRXZhRzQyZVZGNFdEQkVNV0ZVYXk5Skx6TnpkQ3RRDQpVMjVPVmpKTVQzUkhNRVF5UkdKMldVWUtNbk5PWlVSdWNpdGhTWFJIV2tkTk5EaDZkSE5oZG5VMVlUTjRUVXg0Wnl0RVVHOTFSbEp0DQphaXR1U0VoQ2NGRmxORFl6UkVGSVlVUnFNMGxJQ21KaFMxbFdVV1JYWmxsRVdVSlZjMDlsUW1GNloxaE1hVGhuVlc5UGVFNDJhemtyDQpSV05RZVVoMWNIazRTMVZaUkU1dWFVZHZZMnhJU0d3ell3cEJkVzVUVVhweU1sQnlVMjFLSzJ0eWVIZE9aRlZqYW14UFprcHdDajFaDQpWREpCQ2kwdExTMHRSVTVFSUZCSFVDQk5SVk5UUVVkRkxTMHRMUzA9DQotLTAwMDAwMDAwMDAwMDYyMjM4MjA1ZWUzMjFlNzQtLQ0K", + "historyId": "2422243", + "internalDate": "1669273896000" + } +} \ No newline at end of file diff --git a/test/source/mock/google/exported-messages/message-export-184cc6aa8e884397.json b/test/source/mock/google/exported-messages/message-export-184cc6aa8e884397.json new file mode 100644 index 00000000000..bce0d1770c3 --- /dev/null +++ b/test/source/mock/google/exported-messages/message-export-184cc6aa8e884397.json @@ -0,0 +1,76 @@ +{ + "acctEmail": "flowcrypt.compatibility@gmail.com", + "full": { + "id": "184cc6aa8e884397", + "threadId": "184cc6aa8e884397", + "labelIds": [ + "IMPORTANT", + "CATEGORY_PERSONAL", + "Label_15", + "INBOX" + ], + "snippet": "-----BEGIN PGP MESSAGE----- Version: FlowCrypt Email Encryption 8.4.0 Comment: Seamlessly send and receive encrypted email wcFMA0taL/zmLZUBAQ//VIwgNnVXynPl6KdoqhVO6M5uxRPChCm/UtomVDZM", + "payload": { + "partId": "", + "mimeType": "text/plain", + "filename": "", + "headers": [ + { + "name": "X-Gm-Message-State", + "value": "ANoB5plHl3CfIjsDVSGBRTg+1wcc3bWCLjKJSv5zzmOhP8Rrj6XX6jMg pgjDdKx4e+7xoLXGMm0syb6wDRVJPK0AJI8OOEWdh+PSpDU=" + }, + { + "name": "Openpgp", + "value": "id=F6005F1508B0514ACA84224D941B646D3A1F1AB1" + }, + { + "name": "From", + "value": "sender@domain.com" + }, + { + "name": "MIME-Version", + "value": "1.0" + }, + { + "name": "Date", + "value": "Wed, 30 Nov 2022 22:40:51 -0800" + }, + { + "name": "Subject", + "value": "HTTP leak test flowcrypt-security #225" + }, + { + "name": "To", + "value": "flowcrypt.compatibility@gmail.com" + }, + { + "name": "Content-Type", + "value": "text/plain; charset=\"UTF-8\"" + } + ], + "body": { + "size": 2494, + "data": "LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tDQpWZXJzaW9uOiBGbG93Q3J5cHQgRW1haWwgRW5jcnlwdGlvbiA4LjQuMA0KQ29tbWVudDogU2VhbWxlc3NseSBzZW5kIGFuZCByZWNlaXZlIGVuY3J5cHRlZCBlbWFpbA0KDQp3Y0ZNQTB0YUwvem1MWlVCQVEvL1ZJd2dOblZYeW5QbDZLZG9xaFZPNk01dXhSUENoQ20vVXRvbVZEWk0NCnpHYzJHbm0ySEhMUUlWZFdEU05uS2NGMW01RGdzeHpUUkY0R29GY3JhSUx0NWZWdmI4MlRwS2JjT3M0Yw0KUGRjSU5nZ2ZsMmN1aHhXYXNpUzBTWTlPUzVYYitaQVc0OTk3TkpFVytjSzRXOEFGVkMyZFVhU3pVU1pODQptNFpycklRUEJYczVOa1RaK0JJUUlVSnAvUHd4TktVMDZIb0pIQmE3TURabjVyd0tYWUo4ZHZQRlcxUEoNCkFMTUMvcUZzK2cvbzVxV0FLQ3NROFZkY1BjQ1ZDSUhSMjlwaFhwODk4NG9XTks0ZkxEWnN4ZnBwdUp1Zw0KV1AyTFVJSHFTcTUzUEpVZWFaNjg3aEJTMnZFRFk3d0NXd2ZmKzdRUkpYcVhxalMrYldwK2hTS2UzYnBEDQpNM1dDdnlBQnlyRnhyUnhMTDJzMjRtbWp0YktzVXFTcktTUmxBN3RCRVJVdENnMnllejJIck4vUXEwTVQNCkpPcDFYRXhxejJmOVZXQTNtYzRkeXhUZWNxTk1LeXlNSGYrMGkvTTYySmNlVDNjVFNrSktFTC9IUkZrUg0KNlhkNEtWYmdLTStJdCtoWVJQNU9Gd0pEMldFSjZsTCtkdVUzRDVqTUc4SW9kc0lRaWRtUlh6c2ZwaGx4DQpyMUc2d3M1YklXZjloMVBlNENWZmp2SVM4ZkFJMWN1MHpOTnRtakZoNDRvbU4ydG1ZOGloNnpiRlFiYWcNCkFGdzRqMkV6VC94OXR2Qk9WYWJpazg4dmRoTGZKSnVqT24xNHprb3E3dG45S2xidENWNml1ZjRWWnZQcQ0KODJzVlRWcExOV0g1ZEEvc0hLNGRyVkJNcXR5WC96LytYOEJ1czB5cGJzckJ3VXdEdmIxMllQYVpqY1FCDQpELzkxZktuQnRpMXU2NVZaZVlSVU5FNnI5UEVQYUhIQm5lRW1PNHNtckZNZkdWNGhNR2VlOXVoQ1czbEYNCndySmE0K2t3KzBLcUxJWDNCczR0UW5wVjBaUC9jbzlPVk8zbFpEaVlzTVM3Vy94N2RKZkJLclZWalgxUg0KNTZ3NUh1V3crSnpBcnB0UUc5aE1ubE1KU2NGbkJGeEJPdEtISTlZQk1wVkZJcExSeHJNbkt0eGdmVHZxDQppUGNKU2FLU29nNEM0NXJrWVpSSzFSaVpKdjk1T2FDV3ZaZ3I3MWV5cWE1OCszTTNYdFRDTVhCK0ZqN2MNCkZVTVE4WTk0MjVMRUQ0V283ek9xcHJ3aXJQSnBMMElYU2hMUUZKWmhOTlBZb2FmbE5TeHEvTGhzcEd5Zg0KYy9QL1hPeDd2SE0wLzRnU1UvcWJrSFFGTXJ6dElRc0VMSW96WDNScCt5N1JIU0tkMlo2NmtXK1hKNkNmDQpjVjB0TlhFMldBK2VqQUhjSXVUVWNwWVQzdWswazlRbVcyclRFbUg1UjMyUlJ1TjBuRWVUUjVVeldmdHENCmhnNEZBRzhyQXhJSlJ4Y0o5eVFOeVF0dTUrRmFDV3hHemJ4S1VSMHVnc0lDWEV3ajY0WlFHdWZXc3JWZQ0KR3JDRjhtSFdqUFhSaWt0MXQwQWJSa1JYRUpNVzRLOCtwVE9nbGFxcEJGRHVIb0F0ZDlSOVpFV1NVZFNPDQo2aFFVQXh3N2g0K1VGNXhGRkZLck9CZ1kxVWszZHVNV1ZrVVRUSitTTmcyTTFqRXl4OXdVeXJzMXZ6azcNCitPR2N6NGtPRXlJRkhaeTYyZ2h1U0pZSkZGN1U3a3ROVitaVHordWFSalVsRWdad3RzY1RmelJzbkdiZw0KOVdOZEtGNFZkVzBZU2VKUkpnSmc5bVVORHNGZUEybjBreTNrTC9wVkVnRUhRT2lyRTdwdkJ0bXpYNWFSDQpVRThiaE9qck5VQmF1dVB0YW45V1UycXBBOUVXTVAzSTAzRDFiQ1RoMTZDQ09MRTE4Q1NMQmNjdUJyRkwNCktKd3EyNDJqaS9HMHZtWVN0MW1YYTM2MzZ3bVBKVzhsZzhGZUF5M0lkM2N3dlBtbEVnRUhRQ0szWWw5WA0Kd1k0ZXgvNVpXVGVITXNHQi9mKzRiQkFmUkhtOEJtWWxPbDFpTURCTVU1SGV0ekcwbkxoMjhGaW9yK3c0DQpGampHS3RWQU53Qk1URXBmQ0hRNkRERlFZTFUxYlI4TTJjaFVlVitxbU5MQStRRXE4Vkw1dVloUmVMRjQNCjRQOXJDU0JDeVpOSGh4ME1UTkVOaWVrMTFLMmhFWGs3eWRKd0ljckIvSy9HMHBGcG5WYlYrT0N5aStOSA0KdHJaVVhuYTJXNmdrQXFlMndxUU96OVVZNmhRSlZhaExUUXZRLzJHWitYUEFRekNvRUlNdXRNMFpqRGx0DQpjTjZhQ1NGTnlFaExvdHl3UVRKWFpQb3VVOXB1MFYyZ1ltUEEwOENmbWZIT0ZjNHNXZWU2VHBrNnJrQ0sNCnNYZFgwUXQxZVpvd3pKS2YwVGQ5TktnQ3VPRHFaencyTXgyM2VHV055ajlNTDNmb0IwRGdCdmNIZXpGMg0KaVBNdzVqbnVIZGZRSWU4emxManVxVmZxeFRLczhwSVlGOUV3NHIwdlBZOFZqRWpNd0JTckEySXBGeGhCDQpHQzcrbDJwM0JsQlJKNSt6dmZSOTJ3YVBITGVBZml1Um8zQWdydE1RSGkxYm5LanlvRHdPY3NGcXlEdVoNCjV0eVVJRGlUeTNUa09PeVFlMXBSOXhTL2ViMGJDRDdtZnpnUWRyd2x2R2p1S3lvN2lGUU53dlFldW5DWA0KMXpjbGNyL282Q25zZDhzVlp0TkxEUjVxNm5PVC9lTGUyS29vQVN2UWE2N0syeUgyOUQ0bFR6aDNvZ2ZrDQpIbG8zajBJazQ1Uk5xSGl1MVNmaVUra0djaVpmTlYzaE9Kem9nbVQ1eVh5N2J2c3BpdDZ1WFFNNjZpZGcNCmVqSWlNUUc0VTRaYzFPTnd3ZXpNR2MzSllBbUdtV2JReEE9PQ0KPTRNN3INCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0NCg==" + } + }, + "sizeEstimate": 7269, + "historyId": "1379579", + "internalDate": "1669876851000" + }, + "attachments": {}, + "raw": { + "id": "184cc6aa8e884397", + "threadId": "184cc6aa8e884397", + "labelIds": [ + "IMPORTANT", + "CATEGORY_PERSONAL", + "Label_15", + "INBOX" + ], + "snippet": "-----BEGIN PGP MESSAGE----- Version: FlowCrypt Email Encryption 8.4.0 Comment: Seamlessly send and receive encrypted email wcFMA0taL/zmLZUBAQ//VIwgNnVXynPl6KdoqhVO6M5uxRPChCm/UtomVDZM", + "sizeEstimate": 7269, + "raw": "RGVsaXZlcmVkLVRvOiBmbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb20NClJlY2VpdmVkOiBieSAyMDAyOmEwNTo3MzAxOjFjOTQ6YjA6ODk6MzEzOTo5ODUzIHdpdGggU01UUCBpZCBhbDIwY3NwMjExNzA4ZHljOw0KICAgICAgICBXZWQsIDMwIE5vdiAyMDIyIDIyOjQwOjUzIC0wODAwIChQU1QpDQpYLVJlY2VpdmVkOiBieSAyMDAyOmEyZTphODg4OjA6YjA6Mjc3Ojc5NDpjYjg0IHdpdGggU01UUCBpZCBtOC0yMDAyMGEyZWE4ODgwMDAwMDBiMDAyNzcwNzk0Y2I4NG1yMTUwMzk3NTJsanEuNy4xNjY5ODc2ODUyOTgzOw0KICAgICAgICBXZWQsIDMwIE5vdiAyMDIyIDIyOjQwOjUyIC0wODAwIChQU1QpDQpBUkMtU2VhbDogaT0xOyBhPXJzYS1zaGEyNTY7IHQ9MTY2OTg3Njg1MjsgY3Y9bm9uZTsNCiAgICAgICAgZD1nb29nbGUuY29tOyBzPWFyYy0yMDE2MDgxNjsNCiAgICAgICAgYj1Bb3RrMUp6czJ5UE5qcTFLMmttYWRxWjNiWG5lZ0RZd3pXVzdzRXNRNTJPSWhHOFE5NzBzTGJWbXdiMVZOOUJOd2kNCiAgICAgICAgIDNFQWM4TnUwRDJYZC9XK1lYb3BVZkFPRlU2ZTNHQ1RiSG1FNVZpOXplcEhmVTNnL3J4cHB4WStQZHRNVlhrK1NWWHF1DQogICAgICAgICBXNG5yQ3hjclJLRkc5eHNYVm1ZdmsvRGZwTUtIVHZHMHhtUlk3eDkvdVVuZkhpcld5RGc5WmltZXoxT2hOVTFNaFJpaQ0KICAgICAgICAgUGZMRi80aUpmQ2N1UEM1dVhiaUlNYWFEUWw3U05sVE1PZkF4bm5xZ0lhTjdhYkcxNHVCMUNlQzRnb3RSOFFEU0lXL3UNCiAgICAgICAgIHJ5WFZiMExNTVR2RkxtOXlmbGVwMXRzRzJKY256cmFETjNCSFNoSnVMM3JNd2FaUmo3UzFKM1dtNHlNNXJCdHVWRW9tDQogICAgICAgICAvWHhRPT0NCkFSQy1NZXNzYWdlLVNpZ25hdHVyZTogaT0xOyBhPXJzYS1zaGEyNTY7IGM9cmVsYXhlZC9yZWxheGVkOyBkPWdvb2dsZS5jb207IHM9YXJjLTIwMTYwODE2Ow0KICAgICAgICBoPXRvOnN1YmplY3Q6bWVzc2FnZS1pZDpkYXRlOm1pbWUtdmVyc2lvbjpmcm9tOm9wZW5wZ3A6ZGtpbS1zaWduYXR1cmU7DQogICAgICAgIGJoPWpUdDhzbllJVnYzMnRPa24zemp2d1N3ZXk0SlBZTlVVL2gvWGtSdzdoMUE9Ow0KICAgICAgICBiPU5aQlNQdC9Ud2k2YjBNUDVqalM5VkZCbnNLTjhFR3REclREaFRUWXoyUjNQMHd0ZVZrQXR1YW53NGNrYXhvUGRCMw0KICAgICAgICAgWGRzamRwVm9GK082Y3JOeXAvRUhkOTlvdldDdGRic1phK3FaUVN4UnVvNGZWbVgzL0tPLzV6bUtGbnNpZ0M4SFdJWlANCiAgICAgICAgIG9UWFFQOVQxaC9NaitXbHRhZkRuZEEyeWxiSmpwVzN0bGluaGdDbnBJVGh5SGd2ZlkwTVp5R2VyQnFhVEhUZFkxUjIyDQogICAgICAgICBLZ3BKNHBXR3lOdCtoVFNWMVY0QTdyeDJhVzhzVlNtNGtOWG5hWlNjVUoveWlqVy9TYm5USW9NQmoxc0FvV1N3WkFIWg0KICAgICAgICAgalZwcnZtSUxYYTFNNGgzZytjVzdvRTM1ZDYvMXMvWVdRemxYUzJ0bUd6MXpKekpMcDBpS0RIRlFNaFZHRkIvcVBvWTANCiAgICAgICAgIHh3a3c9PQ0KQVJDLUF1dGhlbnRpY2F0aW9uLVJlc3VsdHM6IGk9MTsgbXguZ29vZ2xlLmNvbTsNCiAgICAgICBka2ltPXBhc3MgaGVhZGVyLmk9QGZsb3djcnlwdC5jb20gaGVhZGVyLnM9Z29vZ2xlIGhlYWRlci5iPUh1WTNvVWFaOw0KICAgICAgIHNwZj1wYXNzIChnb29nbGUuY29tOiBkb21haW4gb2YgbWFydEBmbG93Y3J5cHQuY29tIGRlc2lnbmF0ZXMgMjA5Ljg1LjIyMC40MSBhcyBwZXJtaXR0ZWQgc2VuZGVyKSBzbXRwLm1haWxmcm9tPW1hcnRAZmxvd2NyeXB0LmNvbTsNCiAgICAgICBkbWFyYz1wYXNzIChwPVJFSkVDVCBzcD1SRUpFQ1QgZGlzPU5PTkUpIGhlYWRlci5mcm9tPWZsb3djcnlwdC5jb20NClJldHVybi1QYXRoOiA8bWFydEBmbG93Y3J5cHQuY29tPg0KUmVjZWl2ZWQ6IGZyb20gbWFpbC1zb3ItZjQxLmdvb2dsZS5jb20gKG1haWwtc29yLWY0MS5nb29nbGUuY29tLiBbMjA5Ljg1LjIyMC40MV0pDQogICAgICAgIGJ5IG14Lmdvb2dsZS5jb20gd2l0aCBTTVRQUyBpZCAxMy0yMDAyMGFjMjRkNGQwMDAwMDBiMDA0YjE0ZDlmMjIwYXNvcjgxODUyOWxmcC44LjIwMjIuMTEuMzAuMjIuNDAuNTINCiAgICAgICAgZm9yIDxmbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb20-DQogICAgICAgIChHb29nbGUgVHJhbnNwb3J0IFNlY3VyaXR5KTsNCiAgICAgICAgV2VkLCAzMCBOb3YgMjAyMiAyMjo0MDo1MiAtMDgwMCAoUFNUKQ0KUmVjZWl2ZWQtU1BGOiBwYXNzIChnb29nbGUuY29tOiBkb21haW4gb2YgbWFydEBmbG93Y3J5cHQuY29tIGRlc2lnbmF0ZXMgMjA5Ljg1LjIyMC40MSBhcyBwZXJtaXR0ZWQgc2VuZGVyKSBjbGllbnQtaXA9MjA5Ljg1LjIyMC40MTsNCkF1dGhlbnRpY2F0aW9uLVJlc3VsdHM6IG14Lmdvb2dsZS5jb207DQogICAgICAgZGtpbT1wYXNzIGhlYWRlci5pPUBmbG93Y3J5cHQuY29tIGhlYWRlci5zPWdvb2dsZSBoZWFkZXIuYj1IdVkzb1VhWjsNCiAgICAgICBzcGY9cGFzcyAoZ29vZ2xlLmNvbTogZG9tYWluIG9mIG1hcnRAZmxvd2NyeXB0LmNvbSBkZXNpZ25hdGVzIDIwOS44NS4yMjAuNDEgYXMgcGVybWl0dGVkIHNlbmRlcikgc210cC5tYWlsZnJvbT1tYXJ0QGZsb3djcnlwdC5jb207DQogICAgICAgZG1hcmM9cGFzcyAocD1SRUpFQ1Qgc3A9UkVKRUNUIGRpcz1OT05FKSBoZWFkZXIuZnJvbT1mbG93Y3J5cHQuY29tDQpES0lNLVNpZ25hdHVyZTogdj0xOyBhPXJzYS1zaGEyNTY7IGM9cmVsYXhlZC9yZWxheGVkOw0KICAgICAgICBkPWZsb3djcnlwdC5jb207IHM9Z29vZ2xlOw0KICAgICAgICBoPXRvOnN1YmplY3Q6bWVzc2FnZS1pZDpkYXRlOm1pbWUtdmVyc2lvbjpmcm9tOm9wZW5wZ3A6ZnJvbTp0bzpjYw0KICAgICAgICAgOnN1YmplY3Q6ZGF0ZTptZXNzYWdlLWlkOnJlcGx5LXRvOw0KICAgICAgICBiaD1qVHQ4c25ZSVZ2MzJ0T2tuM3pqdndTd2V5NEpQWU5VVS9oL1hrUnc3aDFBPTsNCiAgICAgICAgYj1IdVkzb1VhWituUUZGdW1aTmJCM1FjRlNIcmFzL2RjWDduVVJFUUg2ZklPYjNQWG1lODZBbEkwSG40TXF2bk9qWjMNCiAgICAgICAgIDhDYW0yWmk2Vm5FeGprQlRrbXk0cHdEcGZmODdzVWV1Z2RvcUtSY1Z1ejIvejAycWMyLzI2ZjJuclNlNGREbDJTdFZFDQogICAgICAgICBCVHRYRTgyOHRuQmxrZzlGZlB1Z1NwYVhEUlBmQmpkQjdNZDBVPQ0KWC1Hb29nbGUtREtJTS1TaWduYXR1cmU6IHY9MTsgYT1yc2Etc2hhMjU2OyBjPXJlbGF4ZWQvcmVsYXhlZDsNCiAgICAgICAgZD0xZTEwMC5uZXQ7IHM9MjAyMTAxMTI7DQogICAgICAgIGg9dG86c3ViamVjdDptZXNzYWdlLWlkOmRhdGU6bWltZS12ZXJzaW9uOmZyb206b3BlbnBncA0KICAgICAgICAgOngtZ20tbWVzc2FnZS1zdGF0ZTpmcm9tOnRvOmNjOnN1YmplY3Q6ZGF0ZTptZXNzYWdlLWlkOnJlcGx5LXRvOw0KICAgICAgICBiaD1qVHQ4c25ZSVZ2MzJ0T2tuM3pqdndTd2V5NEpQWU5VVS9oL1hrUnc3aDFBPTsNCiAgICAgICAgYj1YbzVIbVIrTFduSVFDSSt2S2YrSlE3NU9LbVZDdTFWNmFRNGVObG84TGRoRU0vK0Z2RVI0anQyRUtVeEVhaDdiSEMNCiAgICAgICAgIFlyV3Vwcndpc090Z05oTCtoWUpqWUZlNi9IOXpVL2FTWnJsUjlmTlU1NW4yaGo1bHBWYzA4d0xObUVpQTdFQ0VuNUpDDQogICAgICAgICBSR2VPdzZ0eW9FWGQvZFcrVGpveHlCVkdnWTFqNzBLd0ExSGtRN0xBWEdud3pJbTJnZjlJckk5clVxN2dQVURTVmdONA0KICAgICAgICAga0wwMGhIZUNWSndsV00zVVFIbldIeEFEVVFJZ0p0d0QyS2hPZEFnVjI5Y1ppTldleGRJYWxUSjUzcWFQQVRIWithNXUNCiAgICAgICAgICt5MXJyWGFnQlYzZmdKOTF6RmNseU4vUVkvUEY1R1NjSnFqeU5BVTZFNWMwYzZkRXZGZTJydXUza3M0eXRXSGxzNGlqDQogICAgICAgICA2NHhnPT0NClgtR20tTWVzc2FnZS1TdGF0ZTogQU5vQjVwbEhsM0NmSWpzRFZTR0JSVGcrMXdjYzNiV0NMaktKU3Y1enptT2hQOFJyajZYWDZqTWcNCglwZ2pEZEt4NGUrN3hvTFhHTW0wc3liNndEUlZKUEswQUpJOE9PRVdkaCtQU3BEVT0NClgtR29vZ2xlLVNtdHAtU291cmNlOiBBQTBtcWY0ejdOQm00U2dxcFZ0ZEp6a3hIZUVWR1JjUTlFdXM0STVyYzNnS1JvL1NpbGhUeHBjcThJb2JDT05lZCtDSUxZWkJCa1hSdmJ6S29kMVZ1UlpaVDlNPQ0KWC1SZWNlaXZlZDogYnkgMjAwMjphMDU6NjUxMjozY2E5OmIwOjRiNToxMjRkOmViNmIgd2l0aCBTTVRQIGlkDQogaDQxLTIwMDIwYTA1NjUxMjNjYTkwMGIwMDRiNTEyNGRlYjZibXI2NTc1MzY4bGZ2LjU3MS4xNjY5ODc2ODUxODI3OyBXZWQsIDMwDQogTm92IDIwMjIgMjI6NDA6NTEgLTA4MDAgKFBTVCkNClJlY2VpdmVkOiBmcm9tIDcxNzI4NDczMDI0NCBuYW1lZCB1bmtub3duIGJ5IGdtYWlsYXBpLmdvb2dsZS5jb20gd2l0aA0KIEhUVFBSRVNUOyBXZWQsIDMwIE5vdiAyMDIyIDIyOjQwOjUxIC0wODAwDQpPcGVucGdwOiBpZD1GNjAwNUYxNTA4QjA1MTRBQ0E4NDIyNEQ5NDFCNjQ2RDNBMUYxQUIxDQpGcm9tOiBNYXJ0IGF0IEZsb3dDcnlwdCA8bWFydEBmbG93Y3J5cHQuY29tPg0KTUlNRS1WZXJzaW9uOiAxLjANCkRhdGU6IFdlZCwgMzAgTm92IDIwMjIgMjI6NDA6NTEgLTA4MDANCk1lc3NhZ2UtSUQ6IDxDQU12ZDg1NXRaM0Q1Vmh1cVNRaVZyTDVydURtR3B1R2FYPWlzZUxZYXYzTzJrUXA9VFFAbWFpbC5nbWFpbC5jb20-DQpTdWJqZWN0OiBIVFRQIGxlYWsgdGVzdCBmbG93Y3J5cHQtc2VjdXJpdHkgIzIyNQ0KVG86IEZsb3dDcnlwdCBDb21wYXRpYmlsaXR5IDxmbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb20-DQpDb250ZW50LVR5cGU6IHRleHQvcGxhaW47IGNoYXJzZXQ9IlVURi04Ig0KDQotLS0tLUJFR0lOIFBHUCBNRVNTQUdFLS0tLS0NClZlcnNpb246IEZsb3dDcnlwdCBFbWFpbCBFbmNyeXB0aW9uIDguNC4wDQpDb21tZW50OiBTZWFtbGVzc2x5IHNlbmQgYW5kIHJlY2VpdmUgZW5jcnlwdGVkIGVtYWlsDQoNCndjRk1BMHRhTC96bUxaVUJBUS8vVkl3Z05uVlh5blBsNktkb3FoVk82TTV1eFJQQ2hDbS9VdG9tVkRaTQ0KekdjMkdubTJISExRSVZkV0RTTm5LY0YxbTVEZ3N4elRSRjRHb0ZjcmFJTHQ1ZlZ2YjgyVHBLYmNPczRjDQpQZGNJTmdnZmwyY3VoeFdhc2lTMFNZOU9TNVhiK1pBVzQ5OTdOSkVXK2NLNFc4QUZWQzJkVWFTelVTWk4NCm00WnJySVFQQlhzNU5rVForQklRSVVKcC9Qd3hOS1UwNkhvSkhCYTdNRFpuNXJ3S1hZSjhkdlBGVzFQSg0KQUxNQy9xRnMrZy9vNXFXQUtDc1E4VmRjUGNDVkNJSFIyOXBoWHA4OTg0b1dOSzRmTERac3hmcHB1SnVnDQpXUDJMVUlIcVNxNTNQSlVlYVo2ODdoQlMydkVEWTd3Q1d3ZmYrN1FSSlhxWHFqUytiV3AraFNLZTNicEQNCk0zV0N2eUFCeXJGeHJSeExMMnMyNG1tanRiS3NVcVNyS1NSbEE3dEJFUlV0Q2cyeWV6MkhyTi9RcTBNVA0KSk9wMVhFeHF6MmY5VldBM21jNGR5eFRlY3FOTUt5eU1IZiswaS9NNjJKY2VUM2NUU2tKS0VML0hSRmtSDQo2WGQ0S1ZiZ0tNK0l0K2hZUlA1T0Z3SkQyV0VKNmxMK2R1VTNENWpNRzhJb2RzSVFpZG1SWHpzZnBobHgNCnIxRzZ3czViSVdmOWgxUGU0Q1ZmanZJUzhmQUkxY3Uwek5OdG1qRmg0NG9tTjJ0bVk4aWg2emJGUWJhZw0KQUZ3NGoyRXpUL3g5dHZCT1ZhYmlrODh2ZGhMZkpKdWpPbjE0emtvcTd0bjlLbGJ0Q1Y2aXVmNFZadlBxDQo4MnNWVFZwTE5XSDVkQS9zSEs0ZHJWQk1xdHlYL3ovK1g4QnVzMHlwYnNyQndVd0R2YjEyWVBhWmpjUUINCkQvOTFmS25CdGkxdTY1VlplWVJVTkU2cjlQRVBhSEhCbmVFbU80c21yRk1mR1Y0aE1HZWU5dWhDVzNsRg0Kd3JKYTQra3crMEtxTElYM0JzNHRRbnBWMFpQL2NvOU9WTzNsWkRpWXNNUzdXL3g3ZEpmQktyVlZqWDFSDQo1Nnc1SHVXdytKekFycHRRRzloTW5sTUpTY0ZuQkZ4Qk90S0hJOVlCTXBWRklwTFJ4ck1uS3R4Z2ZUdnENCmlQY0pTYUtTb2c0QzQ1cmtZWlJLMVJpWkp2OTVPYUNXdlpncjcxZXlxYTU4KzNNM1h0VENNWEIrRmo3Yw0KRlVNUThZOTQyNUxFRDRXbzd6T3FwcndpclBKcEwwSVhTaExRRkpaaE5OUFlvYWZsTlN4cS9MaHNwR3lmDQpjL1AvWE94N3ZITTAvNGdTVS9xYmtIUUZNcnp0SVFzRUxJb3pYM1JwK3k3UkhTS2QyWjY2a1crWEo2Q2YNCmNWMHROWEUyV0ErZWpBSGNJdVRVY3BZVDN1azBrOVFtVzJyVEVtSDVSMzJSUnVOMG5FZVRSNVV6V2Z0cQ0KaGc0RkFHOHJBeElKUnhjSjl5UU55UXR1NStGYUNXeEd6YnhLVVIwdWdzSUNYRXdqNjRaUUd1ZldzclZlDQpHckNGOG1IV2pQWFJpa3QxdDBBYlJrUlhFSk1XNEs4K3BUT2dsYXFwQkZEdUhvQXRkOVI5WkVXU1VkU08NCjZoUVVBeHc3aDQrVUY1eEZGRktyT0JnWTFVazNkdU1XVmtVVFRKK1NOZzJNMWpFeXg5d1V5cnMxdnprNw0KK09HY3o0a09FeUlGSFp5NjJnaHVTSllKRkY3VTdrdE5WK1pUeit1YVJqVWxFZ1p3dHNjVGZ6UnNuR2JnDQo5V05kS0Y0VmRXMFlTZUpSSmdKZzltVU5Ec0ZlQTJuMGt5M2tML3BWRWdFSFFPaXJFN3B2QnRtelg1YVINClVFOGJoT2pyTlVCYXV1UHRhbjlXVTJxcEE5RVdNUDNJMDNEMWJDVGgxNkNDT0xFMThDU0xCY2N1QnJGTA0KS0p3cTI0MmppL0cwdm1ZU3QxbVhhMzYzNndtUEpXOGxnOEZlQXkzSWQzY3d2UG1sRWdFSFFDSzNZbDlYDQp3WTRleC81WldUZUhNc0dCL2YrNGJCQWZSSG04Qm1ZbE9sMWlNREJNVTVIZXR6RzBuTGgyOEZpb3IrdzQNCkZqakdLdFZBTndCTVRFcGZDSFE2RERGUVlMVTFiUjhNMmNoVWVWK3FtTkxBK1FFcThWTDV1WWhSZUxGNA0KNFA5ckNTQkN5Wk5IaHgwTVRORU5pZWsxMUsyaEVYazd5ZEp3SWNyQi9LL0cwcEZwblZiVitPQ3lpK05IDQp0clpVWG5hMlc2Z2tBcWUyd3FRT3o5VVk2aFFKVmFoTFRRdlEvMkdaK1hQQVF6Q29FSU11dE0wWmpEbHQNCmNONmFDU0ZOeUVoTG90eXdRVEpYWlBvdVU5cHUwVjJnWW1QQTA4Q2ZtZkhPRmM0c1dlZTZUcGs2cmtDSw0Kc1hkWDBRdDFlWm93ekpLZjBUZDlOS2dDdU9EcVp6dzJNeDIzZUdXTnlqOU1MM2ZvQjBEZ0J2Y0hlekYyDQppUE13NWpudUhkZlFJZTh6bExqdXFWZnF4VEtzOHBJWUY5RXc0cjB2UFk4VmpFak13QlNyQTJJcEZ4aEINCkdDNytsMnAzQmxCUko1K3p2ZlI5MndhUEhMZUFmaXVSbzNBZ3J0TVFIaTFibktqeW9Ed09jc0ZxeUR1Wg0KNXR5VUlEaVR5M1RrT095UWUxcFI5eFMvZWIwYkNEN21memdRZHJ3bHZHanVLeW83aUZRTnd2UWV1bkNYDQoxemNsY3IvbzZDbnNkOHNWWnROTERSNXE2bk9UL2VMZTJLb29BU3ZRYTY3SzJ5SDI5RDRsVHpoM29nZmsNCkhsbzNqMElrNDVSTnFIaXUxU2ZpVStrR2NpWmZOVjNoT0p6b2dtVDV5WHk3YnZzcGl0NnVYUU02NmlkZw0KZWpJaU1RRzRVNFpjMU9Od3dlek1HYzNKWUFtR21XYlF4QT09DQo9NE03cg0KLS0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQ0K", + "historyId": "1379579", + "internalDate": "1669876851000" + } +} \ No newline at end of file diff --git a/test/source/mock/google/exported-messages/message-export-1850b93d7772173c.json b/test/source/mock/google/exported-messages/message-export-1850b93d7772173c.json new file mode 100644 index 00000000000..d5aa7d6651d --- /dev/null +++ b/test/source/mock/google/exported-messages/message-export-1850b93d7772173c.json @@ -0,0 +1,150 @@ +{ + "acctEmail": "flowcrypt.compatibility@gmail.com", + "full": { + "id": "1850b93d7772173c", + "threadId": "1850b93d7772173c", + "labelIds": [ + "Label_15", + "SENT", + "INBOX" + ], + "snippet": "", + "payload": { + "partId": "", + "mimeType": "multipart/encrypted", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"----sinikael-?=_1-16709365149630.18248513079448114\"" + }, + { + "name": "Openpgp", + "value": "id=E8F0517BA6D7DAB6081C96E4ADAC279C95093207" + }, + { + "name": "From", + "value": "sender@domain.com" + }, + { + "name": "To", + "value": "flowcrypt.compatibility@gmail.com" + }, + { + "name": "Subject", + "value": "test message for flowcrypt-browser/issues/4759" + }, + { + "name": "Date", + "value": "Tue, 13 Dec 2022 05:01:55 -0800" + }, + { + "name": "MIME-Version", + "value": "1.0" + } + ], + "body": { + "size": 0 + }, + "parts": [ + { + "partId": "0", + "mimeType": "application/pgp-encrypted", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "application/pgp-encrypted; name=" + }, + { + "name": "Content-Description", + "value": "PGP/MIME version identification" + }, + { + "name": "Content-Disposition", + "value": "attachment" + }, + { + "name": "X-Attachment-Id", + "value": "f_aDBEBaLqZQSTYnRfhbCOYLFdKyAdRZ@flowcrypt" + }, + { + "name": "Content-Id", + "value": "" + }, + { + "name": "Content-Transfer-Encoding", + "value": "base64" + } + ], + "body": { + "attachmentId": "ANGjdJ8d1ynEZlmnriWIKSMVqG5k2GCBNOH62a1hTYRH21F0svPptVzzDzfOS1mugyIGmE8qztMMgdxiq-ooNwcv8sGLjjZTnHVTA7SZ0W6jPM7zPyR1lsSYBxq1zFeFXxHwDNrrw1eYA5owfjbealYmTFL0U4l51Pozv8l0cLdX4NQU6wdRLA5Uu7RZdWYlZy-XXwt92kSZnAoOXOyf8zHieGtUCYyuieQvqVAHO4G6JNWmNdwjfhxonr63SSGD41pJAknVkXuj69U0am8aNzEWFeVqhiapcXaQdM7uRiZBjhgWyq4eNyA5p1DXhQrP__g9d7EMThKve5Hm3XssMQ4t83hPJyoMBi2OOnLzpQSHciq_7gJAv3aYusIolzbU33yzQrfhQP9KWh5dU_Gl", + "size": 10 + } + }, + { + "partId": "1", + "mimeType": "application/octet-stream", + "filename": "encrypted.asc", + "headers": [ + { + "name": "Content-Type", + "value": "application/octet-stream; name=encrypted.asc" + }, + { + "name": "Content-Description", + "value": "OpenPGP encrypted message" + }, + { + "name": "Content-Disposition", + "value": "inline; filename=encrypted.asc" + }, + { + "name": "X-Attachment-Id", + "value": "f_LdylnEtSfBbZxlfwEwycUjqMlkuUzH@flowcrypt" + }, + { + "name": "Content-Id", + "value": "" + }, + { + "name": "Content-Transfer-Encoding", + "value": "base64" + } + ], + "body": { + "attachmentId": "ANGjdJ95J8SbKyveS3aPsVZKkKAmsxgPAwEOKjDJLWgTlS1U9ECE8eLzcgG2KZqP_ADRKfcJFp_JnKTEdK3-UL2Fp79uKDdcJVKJJhlHsQG8V-6EbT3bToZjYGnTC4KBjndRrL3BDota-ERGl9g5X4a_KN7qxxRJhZW6CiBXrGDdglqhUu85U6lJcdvU6O9Ep5cecCI5CjBEKEk7Q3NHnWCTpxB9Jo9lQoaTuH3pTz2I_NocQ2FpJspyvu0AUP0ZXEWHmzp2eCcGRZYts_vE4NxUGG-49KzGFJiOIw3s1Tu3DPnfe61j7c-FNwR3qh5Sz07VNloly24T6_GHzbVyHk_-xDPfZG4TsWbJAjReqrwNyF4mo1WAEFuHuJMj5XvZUxq0bagGB6LCeA3bumxy", + "size": 5660 + } + } + ] + }, + "sizeEstimate": 9182, + "historyId": "1380609", + "internalDate": "1670936515000" + }, + "attachments": { + "ANGjdJ8d1ynEZlmnriWIKSMVqG5k2GCBNOH62a1hTYRH21F0svPptVzzDzfOS1mugyIGmE8qztMMgdxiq-ooNwcv8sGLjjZTnHVTA7SZ0W6jPM7zPyR1lsSYBxq1zFeFXxHwDNrrw1eYA5owfjbealYmTFL0U4l51Pozv8l0cLdX4NQU6wdRLA5Uu7RZdWYlZy-XXwt92kSZnAoOXOyf8zHieGtUCYyuieQvqVAHO4G6JNWmNdwjfhxonr63SSGD41pJAknVkXuj69U0am8aNzEWFeVqhiapcXaQdM7uRiZBjhgWyq4eNyA5p1DXhQrP__g9d7EMThKve5Hm3XssMQ4t83hPJyoMBi2OOnLzpQSHciq_7gJAv3aYusIolzbU33yzQrfhQP9KWh5dU_Gl": { + "data": "VmVyc2lvbjogMQ", + "size": 10 + }, + "ANGjdJ95J8SbKyveS3aPsVZKkKAmsxgPAwEOKjDJLWgTlS1U9ECE8eLzcgG2KZqP_ADRKfcJFp_JnKTEdK3-UL2Fp79uKDdcJVKJJhlHsQG8V-6EbT3bToZjYGnTC4KBjndRrL3BDota-ERGl9g5X4a_KN7qxxRJhZW6CiBXrGDdglqhUu85U6lJcdvU6O9Ep5cecCI5CjBEKEk7Q3NHnWCTpxB9Jo9lQoaTuH3pTz2I_NocQ2FpJspyvu0AUP0ZXEWHmzp2eCcGRZYts_vE4NxUGG-49KzGFJiOIw3s1Tu3DPnfe61j7c-FNwR3qh5Sz07VNloly24T6_GHzbVyHk_-xDPfZG4TsWbJAjReqrwNyF4mo1WAEFuHuJMj5XvZUxq0bagGB6LCeA3bumxy": { + "data": "LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tDQpWZXJzaW9uOiBGbG93Q3J5cHQgRW1haWwgRW5jcnlwdGlvbiA4LjMuOA0KQ29tbWVudDogU2VhbWxlc3NseSBzZW5kIGFuZCByZWNlaXZlIGVuY3J5cHRlZCBlbWFpbA0KDQp3Y0ZNQTB0YUwvem1MWlVCQVEvL1pyV0drQ0RoSG1ER0VLNWMxVTY2NFRubi9HUHdLYnhabERNb0xaS2UNCkNMUUxqS3FuYkw4MHByKzZKem1yRHhnOXdmRHBTT1ZUbVF4Z05LSlU2SVVMR0RDOGs3UkR5NmxiTU1ydQ0KUC9JR29NbFlPWmZSeTh2OVlKZkRpczhTRWIrckR4K1dubllzVUdhQTVVUFUvS0swZC9nbjdueGNkVk5BDQpBS0QxaEY4dFNrdTRPMW9BekRpNnF5M010cVp3NjI5NmltZkl4eGswVGh0aE9ZcGdjM2tNaGM1MjBaZFENCkxvNDhoNm9Fbmt2WmkrWVVHQlZVV1JzTktURGRpdWZvTnBpWGtpcENxODhTOE9KMHdsck5zV3lyVW4xcw0KOG52dEQzWjRvUFBsRm83TlVzbmZjTll3QTFkWk5FV2lrWklvZkNsQ0xQWkZhQUR1SStsTTBKa082ZVRhDQpWVzNPc3I2WVg1VW9neXB5M1RBcHFXRzYrOHpJTFJGVi9UeEZVWm5XQ282djJyMWlPRS9MTWE5UGpWMSsNCjdDWWYvM0pEd1Mwd2czZGM4KzFOLzMzclJZc3JmNzdJWHpjdmNyaERpUVhQTEU3eUVnd2NIMUlVTEkrSg0KRHphVkhEbUNXNzJJZC9hSlUrMFE1SlNCb0Z3cjI1TDZiaS9ERXhobmRmeXlLTGFPemc3OGl4ZkxTQS9WDQphVzc5T0VrWWJVRUlTKzJtYnpSaDJyOUd6VFFrS2NTMFBHWm1Mbnd0eWdTRkIzTklyKzNwWUc5SjU4QzQNCmN3MFdvRkRLNDZYODRDSnN2dFJveUZpS05vUXJCWUp1WVJ4VHZHalZzZWdUbG5JSUlxckpBemg1T010SQ0KcFhXU1FBenBmaDhsRHNMVlZLRnhiTHRNTklaK0twSDJsZG9ZUzdSZ0hHN0J3VXdEdmIxMllQYVpqY1FCDQpFQUMvakNFeGRTU1huOVlqWHQrb1N2aVhrUUF6ZURZYXBkYlo5Vk9Kd3FTcFpEV0NTVGJWSmNuRjhBT3kNCjVkU3I4QXRxVXJrL2s1K1pJbW1GTGFOWnBoZnJHY1RkbXZyRGlBZiswNXVEZ2ovWnZYN0Niem5tK3RoUw0KZndyNEJSZk92U3d3aHdTczZnclhmY2VlVmxid2hlMkYveUt3bEVzM1JLaFc5dkorMFhDN0UvalgxZ2gyDQplNUVpSlI3cGFsQTFNVEZGaWxXU2tvdm5SdXZZN2lpemE5RHU0RmpmWGI3WkhJVjE1TUhYNE1qNkRCdTMNCk10UFYyeHM5U0RxZ3hDMStnQ2F0Z2ZBazFjcWxEUTU4SXpqOXM2c3JvNUpMb1lwek12K3ZZTlNEQ052VQ0KQ2krWGE3YUpuaWJocDVvMHlTUmRaWFhEZ0lhR2d4Y09WMFRUWWFQYkpzZ3hXMHNKYXAzL1NiQk9LTVN0DQpQZDFBdWwzNUs3UUJ1U0thYjloWFpydWY4WEtWZUdEd1RKM25DQWRtV2I2M2JoNEZQdFo3S2cxMmtYUFQNCkszczlZWkMrcW5OOXlFT3c1cVRPTVlrMWx5aW45QWVtaHpJcndoMnFUQkNETHdiMzd2b09rUXJXbjFyZg0KeWxpeXJ0dTMycUcyL0NxNzlRZGcwazVmMXFkZldHUjVEZmQzMFo0Rm14K2FIaVdzdHZsb3RwWitBTm9NDQpzUWhSdzRjMnBucTJ4b1V3V2VZcUhIdnlLdzUxaDl2MFlxMk1UMTlUTGJSd2YrUUFWaDYrbnMwRXJPbHANCnB4Y0dyZjlkWlJxeWxuR3JXSkZpY2VQOFI1NEdjc2N0U3VYQXNPT1Q3eUI4Nlc2bXBGZldudWJYQzBXNw0KTjdIMCtSS1FIa0hrdEhMeFlRZURKcDBSeU1GZUEybjBreTNrTC9wVkVnRUhRSCtwNHFwVzlqdWtsSnlODQpLdmgwSWRjOHVrWm5ENTVOaTRiSXpZVkZRM29kTU05UG9Lai9pMXZCZDJYa0VHL0NSN2t3MUFyUU1CN0kNClZidUNPNEFDcGpYVTFmTll1Q25BUFlhRTRtNjFHK0l6a2NIQlRBTkxXaS84NWkyVkFRRVAvUnpPNTBWWQ0KYUZpcXVTZkdCTkhqYmZlUFZaK2FiSDdCOC80VDZCUzVMSmVoVVRTME5iTU5FYTNBNW1zNkcvbTZ2aS9KDQp4VEZ3T3FKcGkxaTc2WXo5NXhwR1RsTGlvbE5YVTJKTC9aQ09LRElOUUsyUk9DeWMxek9Lc1lpamxKNEgNCnpCeEoyRU95R0t1Y2U3TzlCK1lQSThYK1lvY0cwanM2eHJsZGc2cENFbWdGRVh1bHRvdlIwY0RtQ084Rg0KcXZBQWQwd2xVUGRkdmhyTFhZMTRDVzYxUDZOdjVyTUZBdUxLVG9lMUhEbDB5c2p2ZnJOcDJnQ0FGaTg1DQp3SW1jbHE1c0RWbGgzK1NpZ3JHY3VZWjBTY1BRZHhjK3JDLzlubDhZbmtMRDRLWlJCdVBSVmJ0TkFqa2YNCkhFZzQzeW80ZlhGMUZqUzNyVmM4MWNuREVwdHdtMjVwZVhOWTlDbFZ4dktMVGdDcXNGemFLak9ZN3owZA0KSGlaUy8wUFpNSm9zL1BxZGhFLzgrMGZMNE1VbHZraFVWLzdRVUpUVVB1VjcyckhPOWZoYW1IZFhiUUJvDQpQSzJkZDJEeGt4RGg1dnE2Rys4cWR1RjJVeXRuK0ZEcTNQbkhmL2dFOEtoMGwvYVgvaFV4eGZGL3hFMFQNCldEUzFtOVhpSHlQK0ZzMVMvQktOT3ZXN3YxOHJEcDVoVDk0WE1rU2FNMUNRQ2lHWG1SVXpIbnlMVGxHMA0KTGFmdVAwaUIzZ2MxS3RobmhIdTdoSWlObkNTVUV3UlZvTjlOSGUrNHhIcTZkVGdSdTg1ajkwaDFUT3NlDQpyei9KOVZhRGZVeXNObnhtTHVCcUJmcWg3ai9ScERXTWtMN3NsOUZCWm5Lb21wUHFQTzZtVCthQ1NhdzkNCmw3dTBuYW1SL3BGQ0RGVU53Y0ZNQTcyOWRtRDJtWTNFQVJBQXdSQzRISW53czY5cWFLeEU3cU9uWG9EKw0KRUVXbjQ5ZmtBaFFNUlhHZTVXYkUwQ1dDT1FBMjllTHkrNkJvUkp3U1V3eE9tY1RpSGU1cVp3VnRFb2lBDQpvNzI0ay94SDFaODN3ZUU0N1FNdVZSWnUvN0lWNmpNNURlKzRPZy9pN2dRTWlxUWFReDBvTVVlM0hYQmcNCjFnUWV2OHhuSzZJMU4yNUo0Y1RTWXd0Qm4xc25kOUJSMGIybzdkZVBkbWoyVG95bU5oVHJYMGdMMUZMRQ0KMXVJS041Q3ZhR1g1czBSSGVEbFdJckpzK0hJV1FYd3poVDZSdDI0cWMwUkdtdWVManVkTjl4dGx4YjF3DQpHQVBFK29RNWluRityM3UxSm9VZGZNajZGZUFPNHJSdWRRNFRDZXJwWUZ5ekl5K20yWmY4clVqSW5UT08NCm9qYnQydUhqUUxoSDRHaFZIK1B6b0UzRldkM1ZrQkdhY3hVUkFEaDNlTGUyTWRYUm5QejBEdHZNWThveQ0KVy81Q3ZFWnZHK0Jtbzl5ZkJLelE2U0hzNmdoUGVTMWpYbGFNejlabEJXT1pmTDBoOUxHSHJzVVUveUd2DQoza3MvUW50NGk5WTl4ZUtsN3pXWHkwMUVFNjBwVlJlT1FteXk1RHZpWHUyR3FXWWkzUFE1QTFSNlNtQTcNCjZTQmFyL2VFQVB4cEk2TGNuTlAvU1RaNEQ2N2h3RFpYRnpiLys1U2hidUF1enlLQTF3eGxqMkp5RSt5aA0KYXRRRE1RckJ2cloydVhDVUw3d2NEcStscjFLMHcyVGN1ckM3SzRXZUdoYzZGL0p2UVhVcUdITDZ6anBBDQpMQjd3SFQyVDUrdlk4cmlqWGZMODdSYWg1QzVUS0JxUGtBZzhGYVJHeW9mNWVzT1QxL1ZnYTdleDN1L1MNCnhqY0JaQVM5SnN2dXk4bE4wMVJ0OWhzazhUVHpyQkFTTXI3QUJ2S09NeE9QTXQrdHRZVGVmZ2ZjSFhNZg0KV3hwelNDQ0Q0bXd2VmNVTGZqVnh6ZllwVGdpWFFMYkI0eElFRlZZUkpCMHhvdk1sZ3U2NVdQblVCWEZVDQpyMUpIUXVobnRxQ0NvbE9JY3k4NDYxU3E3ZmNTUkdqWTFmQ3hyMTc2UC9DeFpsUU1yRW13L2VXUUU2MisNCnIvZmpCL2RrSlJjR0w3SGdTNkdwWXFDWW5POUhBMGdNSndUT1lSKzFOcFJ6c3pxWE1lbkFBREJmWWpXVw0KTVhnKzhSRzRFUHJxODRTNWhLeEJWUGVMbkJFbGhnVEZ2RkdKQzlOU3c0SzNnTnkxdEFlREd1TXlSd2xDDQpLUlZ2ZFcxTm82TG1GdFY4OHVRNk91Uno5b1c2aEt6YW03OTNQaVRiRm5wS3ZPUEp0VTlRT0RQdDNwNTgNCnhuNFlaMU1qVVVDa1JET0ZiYkxvNzhPeERVR0JBenlPZ1ZTZS9JN1h1ZHZKWXlDMWtzeGlidndMRFlobA0KREM2TEFqbVJ5ZzFYUDRiVlQ2NWdIRzZBMjhvK1RWKzZiYVkveW5xbWhtQmVnZWZiYklkaEF4WVFVWEoxDQpuYUk2UVRXcmliVXkvRHQyQ0M4dzVtSThFRjlMeVdaT1VwM2JHNGpBU0pZZ1lReGR4b2RJKzdVZ2MvbHcNClNXTm52YS9oUmFuNTZPUVQ3ZGpQNE82OGdlSkx1Rkh5aTNHMXhFZVNzWlIrSnkxeFNZaEtIZ1FjeXRESw0KRG1BZDlpN1MxYWtjVTNlUTZGV1NrZzlSM3ZPeUszUXNFK2dEQnZWdEg1cGRxN3RlajgvamdsR3ppODNMDQptdnpNV1ptT3FVVEpreXFUYWNNMnN5NTFoV1orZkRhK0RoODRCblZ4a1cxL3BZd0grZWhTWHJLMGZrRDgNCndwdzh1Rk04aVRicHZGbE9LbkRUN0RFUFN6aHR3SjZwcHJ0Z2RkZHBqOEowWU5weHh1OWxrbVBKMExKdA0KVzlReTF1VG0ycnl3bjNIZVowWFVwYSt1WVVCOURvWHlvZmpkT29rVCtISCtmeUZvT1JaSkJVVGF0UGQ4DQp5NkpBdTR2VStYZElQYnpIcTNFdjNvMHBnTDU1WmxrSCtQMmJ6dzdRNElHSE5mZUZwU0poS3NWQUtjbkUNClQyUVdraE5iTktlaTlRZFdHbStsZ3lyd0l1QThwcFZDSXJzQXM2NHUzbTkycmVQVDlDUTU3OFh2WnZSaw0KTGtLL21hNmZYVDNHVVpSeHB5NWp2dXo4dVBZVXRYdWxVUkxkZE54aEJmZDJjU3NKQzd0d0Nnck1wNmlpDQpmQi94R3pWcjFodGZwN05hamVsdmFkY1JtdWpiMHhuTEw0WktteDZyQnV2T3RPL2NEdlNWMmdMdk9uWHANCk5lMTU4ZkMvZGJOeGNZQUd6NUN2eXhlaVBEMWlsbWtibExVQlExc1ZCMW5YakNPNCtLa2poNDhVUXZYbg0Kczl6T0dUcmRUVUQxSGFIQVo4L2lYcDcvUmNtYkxGL1BLdWRvakdPakhHcGNpRmFJWG8vZHlXU2puMFN2DQpzUk15bVlYZXJrSFNTVXZIMXMwM1lHeWpJUHBzRWRiNGZTbGlOYmNTL094RzBXNEZoTFRablFPRWN0b3ANCjRmQlF5dTBOKzhrVjdLRm9ua0tsRW9CSGdhK1JzQWQ5SmxqYTMxWWc3ZFdhUlZ1bFA0alVxSyt3TlArSg0KV1hzS1N3RDF5a1dRMGlKZjI1cEk4UG9BcUF3MVNUY2ppT0xtT3BHcm16S2x1WkZKcndZbTBoRHVLRGwvDQptamFldXoyVkx2Y2ttNHFPMmM2QTZ6eHNpeGhaZzNyLzlvM2F4a2t4TjlJWHYzVkZaQjJrdnh5SU9vSUcNCnZQU1F6L0dYekxvajZGbG13c1ptRUNCYmtVRHNPb2ZCOVBzc2NWeFRmZTBzZlRxOW1FbTlDbkp1amwzQQ0KVkpJN1VCNHc4c1A5U3JOTThUbUIzRGZ0Ukt5MWdWdHczRXMveDZUT05DeENwVnc4VndjaE9FTXZXelFoDQpYMFY2ZUtzZDFGUDJtRFVOa1p0YXVtd21ZZDg0SU9JQndEQyt4Ly9kaXJ3bFB1WEJzWis5R0xxc1hrZ3ENCk5INzRnM1lKU0NWWWhSeW1vSjdMTWJ6ZG9XdCtwUW9KaEM1Tmd1WlA0VWZqOStXbEFFM1lXUkswQzZZcg0KTjRoSWp3K3RYWlltRUI3R0Y0eXlRaWJBUFRDaStWeVIvOFFESUR2MEJaditiYkN1WjJ4cTdvNCtPK3c4DQo4K1VJOUwrUXVwaGRhajM0V2ZldXBpUHkvSUpiZFVYcnBwU2VYbUd3OGRDQjFaaDBkQ0pYU0pVTFhKMEgNCmd4R1VZMFZKUEF2aERZV0phVE9HRjZjdU82dE80KzFrYllVL3RGYkF1ZmwzR1A3V0FlcnZBWlZCV3Vneg0KL0Y1ZDhLbVp3MVp3RWZiSEhhQlk0dnNjNHl2K3ZvbU94dVR5NmJ4b3VNTU4wZTRKYks2RzZMb3lBd0ZUDQo0R2w3MTV6RnJIZC9LZ2djVzVpSWphVDJvbXB5ZGpGREV5NjNjT2xTaDJ0c1pyYTVvUUluMWJVSVJyY0UNCjBkWWhUd2htaDVLM21HUmsraWpkTWRpc01MUFRIa3J2N0JWY011b0E2WDJsNjlBLzNMUHhZc1F3ck9tVg0KdWloOThqMWRsenBiMEtNTUZEZ2NEYm56ZitlWmFieUR2WlN2a2MwOWVTRUVoc2E4SW42T3g5MUxwbVYyDQpQU1hCeXN1a1RacHBUdlhhY3hwYVdUZ0xYMnZRNUVCSCt2bEMzRzRrUlRrL0Z0VmxTTzJRWUxMWXM0elUNCkpzTnNpYXo4akQrbFNlbFAxemxxUzFDdEYvUGt0cEhDTy9aK003WW00N3F2eHVyallpcGdyeTlZbG1VcQ0KMzQ1ODJFOGpYM2ZxRXZFRjU5SXRqdmFnelRlTldaYVNXYURRMGxPcytIUDlkSjFqd2c4RmxTVjM0OFFrDQpJL3dXSWdlcWtyTHFQNExYTWw2OTdFdmVtK2xSQ3JCQ2crNlZxTjQ3eGJYZFhOb2NCbDBQeVFLMXZ0ak4NCkMrMlpiU0VUbzluM3JtOTdPMXpaR2N5emJHNHFhVjZCQ21LVTREYnQNCj1yaWtmDQotLS0tLUVORCBQR1AgTUVTU0FHRS0tLS0tDQo", + "size": 5660 + } + }, + "raw": { + "id": "1850b93d7772173c", + "threadId": "1850b93d7772173c", + "labelIds": [ + "Label_15", + "SENT", + "INBOX" + ], + "snippet": "", + "sizeEstimate": 9182, + "raw": "UmVjZWl2ZWQ6IGZyb20gNzE3Mjg0NzMwMjQ0DQoJbmFtZWQgdW5rbm93bg0KCWJ5IGdtYWlsYXBpLmdvb2dsZS5jb20NCgl3aXRoIEhUVFBSRVNUOw0KCVR1ZSwgMTMgRGVjIDIwMjIgMDU6MDE6NTUgLTA4MDANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L2VuY3J5cHRlZDsgcHJvdG9jb2w9ImFwcGxpY2F0aW9uL3BncC1lbmNyeXB0ZWQiOw0KIGJvdW5kYXJ5PSItLS0tc2luaWthZWwtPz1fMS0xNjcwOTM2NTE0OTYzMC4xODI0ODUxMzA3OTQ0ODExNCINCk9wZW5wZ3A6IGlkPUU4RjA1MTdCQTZEN0RBQjYwODFDOTZFNEFEQUMyNzlDOTUwOTMyMDcNCkZyb206IEZsb3dDcnlwdCBDb21wYXRpYmlsaXR5IDxmbG93Y3J5cHQuY29tcGF0aWJpbGl0eUBnbWFpbC5jb20-DQpUbzogRmxvd0NyeXB0IENvbXBhdGliaWxpdHkgPGZsb3djcnlwdC5jb21wYXRpYmlsaXR5QGdtYWlsLmNvbT4NClN1YmplY3Q6IHRlc3QgbWVzc2FnZSBmb3IgZmxvd2NyeXB0LWJyb3dzZXIvaXNzdWVzLzQ3NTkNCkRhdGU6IFR1ZSwgMTMgRGVjIDIwMjIgMDU6MDE6NTUgLTA4MDANCk1lc3NhZ2UtSWQ6IDxDQUtidUxUcHh6NSt5QTRpT0RQemFISkF3R01wb0NzX1M4K2IzX3VnZmFRUVlPeWViZUFAbWFpbC5nbWFpbC5jb20-DQpNSU1FLVZlcnNpb246IDEuMA0KDQotLS0tLS1zaW5pa2FlbC0_PV8xLTE2NzA5MzY1MTQ5NjMwLjE4MjQ4NTEzMDc5NDQ4MTE0DQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL3BncC1lbmNyeXB0ZWQ7IG5hbWU9DQpDb250ZW50LURlc2NyaXB0aW9uOiBQR1AvTUlNRSB2ZXJzaW9uIGlkZW50aWZpY2F0aW9uDQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50DQpYLUF0dGFjaG1lbnQtSWQ6IGZfYURCRUJhTHFaUVNUWW5SZmhiQ09ZTEZkS3lBZFJaQGZsb3djcnlwdA0KQ29udGVudC1JZDogPGZfYURCRUJhTHFaUVNUWW5SZmhiQ09ZTEZkS3lBZFJaQGZsb3djcnlwdD4NCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NA0KDQpWbVZ5YzJsdmJqb2dNUT09DQotLS0tLS1zaW5pa2FlbC0_PV8xLTE2NzA5MzY1MTQ5NjMwLjE4MjQ4NTEzMDc5NDQ4MTE0DQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL29jdGV0LXN0cmVhbTsgbmFtZT1lbmNyeXB0ZWQuYXNjDQpDb250ZW50LURlc2NyaXB0aW9uOiBPcGVuUEdQIGVuY3J5cHRlZCBtZXNzYWdlDQpDb250ZW50LURpc3Bvc2l0aW9uOiBpbmxpbmU7IGZpbGVuYW1lPWVuY3J5cHRlZC5hc2MNClgtQXR0YWNobWVudC1JZDogZl9MZHlsbkV0U2ZCYlp4bGZ3RXd5Y1VqcU1sa3VVekhAZmxvd2NyeXB0DQpDb250ZW50LUlkOiA8Zl9MZHlsbkV0U2ZCYlp4bGZ3RXd5Y1VqcU1sa3VVekhAZmxvd2NyeXB0Pg0KQ29udGVudC1UcmFuc2Zlci1FbmNvZGluZzogYmFzZTY0DQoNCkxTMHRMUzFDUlVkSlRpQlFSMUFnVFVWVFUwRkhSUzB0TFMwdERRcFdaWEp6YVc5dU9pQkdiRzkzUTNKNWNIUWdSVzFoYVd3Z1JXNWoNCmNubHdkR2x2YmlBNExqTXVPQTBLUTI5dGJXVnVkRG9nVTJWaGJXeGxjM05zZVNCelpXNWtJR0Z1WkNCeVpXTmxhWFpsSUdWdVkzSjUNCmNIUmxaQ0JsYldGcGJBMEtEUXAzWTBaTlFUQjBZVXd2ZW0xTVdsVkNRVkV2TDFweVYwZHJRMFJvU0cxRVIwVkxOV014VlRZMk5GUnUNCmJpOUhVSGRMWW5oYWJFUk5iMHhhUzJVTkNrTk1VVXhxUzNGdVlrdzRNSEJ5S3paS2VtMXlSSGhuT1hkbVJIQlRUMVpVYlZGNFowNUwNClNsVTJTVlZNUjBSRE9HczNVa1I1Tm14aVRVMXlkUTBLVUM5SlIyOU5iRmxQV21aU2VUaDJPVmxLWmtScGN6aFRSV0lyY2tSNEsxZHUNCmJsbHpWVWRoUVRWVlVGVXZTMHN3WkM5bmJqZHVlR05rVms1QkRRcEJTMFF4YUVZNGRGTnJkVFJQTVc5QmVrUnBObkY1TTAxMGNWcDMNCk5qSTVObWx0WmtsNGVHc3dWR2gwYUU5WmNHZGpNMnROYUdNMU1qQmFaRkVOQ2t4dk5EaG9ObTlGYm10Mldta3JXVlZIUWxaVlYxSnoNClRrdFVSR1JwZFdadlRuQnBXR3RwY0VOeE9EaFRPRTlLTUhkc2NrNXpWM2x5Vlc0eGN3MEtPRzUyZEVReldqUnZVRkJzUm04M1RsVnoNCmJtWmpUbGwzUVRGa1drNUZWMmxyV2tsdlprTnNRMHhRV2taaFFVUjFTU3RzVFRCS2EwODJaVlJoRFFwV1Z6TlBjM0kyV1ZnMVZXOW4NCmVYQjVNMVJCY0hGWFJ6WXJPSHBKVEZKR1ZpOVVlRVpWV201WFEyODJkakp5TVdsUFJTOU1UV0U1VUdwV01Tc05DamREV1dZdk0wcEUNCmQxTXdkMmN6WkdNNEt6Rk9Mek16Y2xKWmMzSm1OemRKV0hwamRtTnlhRVJwVVZoUVRFVTNlVVZuZDJOSU1VbFZURWtyU2cwS1JIcGgNClZraEViVU5YTnpKSlpDOWhTbFVyTUZFMVNsTkNiMFozY2pJMVREWmlhUzlFUlhob2JtUm1lWGxMVEdGUGVtYzNPR2w0Wmt4VFFTOVcNCkRRcGhWemM1VDBWcldXSlZSVWxUS3pKdFlucFNhREp5T1VkNlZGRnJTMk5UTUZCSFdtMU1ibmQwZVdkVFJrSXpUa2x5S3pOd1dVYzUNClNqVTRRelFOQ21OM01GZHZSa1JMTkRaWU9EUkRTbk4yZEZKdmVVWnBTMDV2VVhKQ1dVcDFXVko0VkhaSGFsWnpaV2RVYkc1SlNVbHgNCmNrcEJlbWcxVDAxMFNRMEtjRmhYVTFGQmVuQm1hRGhzUkhOTVZsWkxSbmhpVEhSTlRrbGFLMHR3U0RKc1pHOVpVemRTWjBoSE4wSjMNClZYZEVkbUl4TWxsUVlWcHFZMUZDRFFwRlFVTXZha05GZUdSVFUxaHVPVmxxV0hRcmIxTjJhVmhyVVVGNlpVUlpZWEJrWWxvNVZrOUsNCmQzRlRjRnBFVjBOVFZHSldTbU51UmpoQlQza05DalZrVTNJNFFYUnhWWEpyTDJzMUsxcEpiVzFHVEdGT1duQm9abkpIWTFSa2JYWnkNClJHbEJaaXN3TlhWRVoyb3ZXblpZTjBOaWVtNXRLM1JvVXcwS1puZHlORUpTWms5MlUzZDNhSGRUY3pabmNsaG1ZMlZsVm14aWQyaGwNCk1rWXZlVXQzYkVWek0xSkxhRmM1ZGtvck1GaEROMFV2YWxneFoyZ3lEUXBsTlVWcFNsSTNjR0ZzUVRGTlZFWkdhV3hYVTJ0dmRtNVMNCmRYWlpOMmxwZW1FNVJIVTBSbXBtV0dJM1draEpWakUxVFVoWU5FMXFOa1JDZFRNTkNrMTBVRll5ZUhNNVUwUnhaM2hETVN0blEyRjANCloyWkJhekZqY1d4RVVUVTRTWHBxT1hNMmMzSnZOVXBNYjFsd2VrMTJLM1paVGxORVEwNTJWUTBLUTJrcldHRTNZVXB1YVdKb2NEVnYNCk1IbFRVbVJhV0ZoRVowbGhSMmQ0WTA5V01GUlVXV0ZRWWtwelozaFhNSE5LWVhBekwxTmlRazlMVFZOMERRcFFaREZCZFd3ek5VczMNClVVSjFVMHRoWWpsb1dGcHlkV1k0V0V0V1pVZEVkMVJLTTI1RFFXUnRWMkkyTTJKb05FWlFkRm8zUzJjeE1tdFlVRlFOQ2tzemN6bFoNCldrTXJjVzVPT1hsRlQzYzFjVlJQVFZsck1XeDVhVzQ1UVdWdGFIcEpjbmRvTW5GVVFrTkVUSGRpTXpkMmIwOXJVWEpYYmpGeVpnMEsNCmVXeHBlWEowZFRNeWNVY3lMME54TnpsUlpHY3dhelZtTVhGa1psZEhValZFWm1Rek1GbzBSbTE0SzJGSWFWZHpkSFpzYjNSd1dpdEINClRtOU5EUXB6VVdoU2R6UmpNbkJ1Y1RKNGIxVjNWMlZaY1VoSWRubExkelV4YURsMk1GbHhNazFVTVRsVVRHSlNkMllyVVVGV2FEWXINCmJuTXdSWEpQYkhBTkNuQjRZMGR5Wmpsa1dsSnhlV3h1UjNKWFNrWnBZMlZRT0ZJMU5FZGpjMk4wVTNWWVFYTlBUMVEzZVVJNE5sYzINCmJYQkdabGR1ZFdKWVF6QlhOdzBLVGpkSU1DdFNTMUZJYTBocmRFaE1lRmxSWlVSS2NEQlNlVTFHWlVFeWJqQnJlVE5yVEM5d1ZrVm4NClJVaFJTQ3R3TkhGd1Z6bHFkV3RzU25sT0RRcExkbWd3U1dSak9IVnJXbTVFTlRWT2FUUmlTWHBaVmtaUk0yOWtUVTA1VUc5TGFpOXANCk1YWkNaREpZYTBWSEwwTlNOMnQzTVVGeVVVMUNOMGtOQ2xaaWRVTlBORUZEY0dwWVZURm1UbGwxUTI1QlVGbGhSVFJ0TmpGSEswbDYNCmEyTklRbFJCVGt4WGFTODROV2t5VmtGUlJWQXZVbnBQTlRCV1dRMEtZVVpwY1hWVFprZENUa2hxWW1abFVGWmFLMkZpU0RkQ09DODANClZEWkNVelZNU21Wb1ZWUlRNRTVpVFU1RllUTkJOVzF6TmtjdmJUWjJhUzlLRFFwNFZFWjNUM0ZLY0dreGFUYzJXWG81Tlhod1IxUnMNClRHbHZiRTVZVlRKS1RDOWFRMDlMUkVsT1VVc3lVazlEZVdNeGVrOUxjMWxwYW14S05FZ05DbnBDZUVveVJVOTVSMHQxWTJVM1R6bEMNCksxbFFTVGhZSzFsdlkwY3dhbk0yZUhKc1pHYzJjRU5GYldkR1JWaDFiSFJ2ZGxJd1kwUnRRMDg0UmcwS2NYWkJRV1F3ZDJ4VlVHUmsNCmRtaHlURmhaTVRSRFZ6WXhVRFpPZGpWeVRVWkJkVXhMVkc5bE1VaEViREI1YzJwMlpuSk9jREpuUTBGR2FUZzFEUXAzU1cxamJIRTENCmMwUldiR2d6SzFOcFozSkhZM1ZaV2pCVFkxQlJaSGhqSzNKREx6bHViRGhaYm10TVJEUkxXbEpDZFZCU1ZtSjBUa0ZxYTJZTkNraEYNClp6UXplVzgwWmxoR01VWnFVek55Vm1NNE1XTnVSRVZ3ZEhkdE1qVndaVmhPV1RsRGJGWjRka3RNVkdkRGNYTkdlbUZMYWs5Wk4zb3cNClpBMEtTR2xhVXk4d1VGcE5TbTl6TDFCeFpHaEZMemdyTUdaTU5FMVZiSFpyYUZWV0x6ZFJWVXBVVlZCMVZqY3lja2hQT1dab1lXMUkNClpGaGlVVUp2RFFwUVN6SmtaREpFZUd0NFJHZzFkbkUyUnlzNGNXUjFSakpWZVhSdUswWkVjVE5RYmtobUwyZEZPRXRvTUd3dllWZ3YNCmFGVjRlR1pHTDNoRk1GUU5DbGRFVXpGdE9WaHBTSGxRSzBaek1WTXZRa3RPVDNaWE4zWXhPSEpFY0RWb1ZEazBXRTFyVTJGTk1VTlINClEybEhXRzFTVlhwSWJubE1WR3hITUEwS1RHRm1kVkF3YVVJeloyTXhTM1JvYm1oSWRUZG9TV2xPYmtOVFZVVjNVbFp2VGpsT1NHVXINCk5IaEljVFprVkdkU2RUZzFhamt3YURGVVQzTmxEUXB5ZWk5S09WWmhSR1pWZVhOT2JuaHRUSFZDY1VKbWNXZzNhaTlTY0VSWFRXdE0NCk4zTnNPVVpDV201TGIyMXdVSEZRVHpadFZDdGhRMU5oZHprTkNtdzNkVEJ1WVcxU0wzQkdRMFJHVlU1M1kwWk5RVGN5T1dSdFJESnQNCldUTkZRVkpCUVhkU1F6UklTVzUzY3pZNWNXRkxlRVUzY1U5dVdHOUVLdzBLUlVWWGJqUTVabXRCYUZGTlVsaEhaVFZYWWtVd1ExZEQNClQxRkJNamxsVEhrck5rSnZVa3AzVTFWM2VFOXRZMVJwU0dVMWNWcDNWblJGYjJsQkRRcHZOekkwYXk5NFNERmFPRE4zWlVVME4xRk4NCmRWWlNXblV2TjBsV05tcE5OVVJsS3pSUFp5OXBOMmRSVFdseFVXRlJlREJ2VFZWbE0waFlRbWNOQ2pGblVXVjJPSGh1U3paSk1VNHkNCk5VbzBZMVJUV1hkMFFtNHhjMjVrT1VKU01HSXliemRrWlZCa2JXb3lWRzk1YlU1b1ZISllNR2RNTVVaTVJRMEtNWFZKUzA0MVEzWmgNClIxZzFjekJTU0dWRWJGZEpja3B6SzBoSlYxRllkM3BvVkRaU2RESTBjV013VWtkdGRXVk1hblZrVGpsNGRHeDRZakYzRFFwSFFWQkYNCksyOVJOV2x1Uml0eU0zVXhTbTlWWkdaTmFqWkdaVUZQTkhKU2RXUlJORlJEWlhKd1dVWjVla2w1SzIweVdtWTRjbFZxU1c1VVQwOE4NCkNtOXFZblF5ZFVocVVVeG9TRFJIYUZaSUsxQjZiMFV6Umxka00xWnJRa2RoWTNoVlVrRkVhRE5sVEdVeVRXUllVbTVRZWpCRWRIWk4NCldUaHZlUTBLVnk4MVEzWkZXblpISzBKdGJ6bDVaa0pMZWxFMlUwaHpObWRvVUdWVE1XcFliR0ZOZWpsYWJFSlhUMXBtVERCb09VeEgNClNISnpWVlV2ZVVkMkRRb3phM012VVc1ME5HazVXVGw0WlV0c04zcFhXSGt3TVVWRk5qQndWbEpsVDFGdGVYazFSSFpwV0hVeVIzRlgNCldXa3pVRkUxUVRGU05sTnRRVGNOQ2paVFFtRnlMMlZGUVZCNGNFazJUR051VGxBdlUxUmFORVEyTjJoM1JGcFlSbnBpTHlzMVUyaGkNCmRVRjFlbmxMUVRGM2VHeHFNa3A1UlN0NWFBMEtZWFJSUkUxUmNrSjJjbG95ZFZoRFZVdzNkMk5FY1N0c2NqRkxNSGN5VkdOMWNrTTMNClN6UlhaVWRvWXpaR0wwcDJVVmhWY1VkSVREWjZhbkJCRFFwTVFqZDNTRlF5VkRVcmRsazRjbWxxV0daTU9EZFNZV2cxUXpWVVMwSngNClVHdEJaemhHWVZKSGVXOW1OV1Z6VDFReEwxWm5ZVGRsZUROMUwxTU5DbmhxWTBKYVFWTTVTbk4yZFhrNGJFNHdNVkowT1doemF6aFUNClZIcHlRa0ZUVFhJM1FVSjJTMDlOZUU5UVRYUXJkSFJaVkdWbVoyWmpTRmhOWmcwS1YzaHdlbE5EUTBRMGJYZDJWbU5WVEdacVZuaDYNClpsbHdWR2RwV0ZGTVlrSTBlRWxGUmxaWlVrcENNSGh2ZGsxc1ozVTJOVmRRYmxWQ1dFWlZEUXB5TVVwSVVYVm9iblJ4UTBOdmJFOUoNClkzazRORFl4VTNFM1ptTlRVa2RxV1RGbVEzaHlNVGMyVUM5RGVGcHNVVTF5UlcxM0wyVlhVVVUyTWlzTkNuSXZabXBDTDJSclNsSmoNClIwdzNTR2RUTmtkd1dYRkRXVzVQT1VoQk1HZE5TbmRVVDFsU0t6Rk9jRko2YzNweFdFMWxia0ZCUkVKbVdXcFhWdzBLVFZobkt6aFMNClJ6UkZVSEp4T0RSVE5XaExlRUpXVUdWTWJrSkZiR2huVkVaMlJrZEtRemxPVTNjMFN6Tm5Ubmt4ZEVGbFJFZDFUWGxTZDJ4RERRcEwNClVsWjJaRmN4VG04MlRHMUdkRlk0T0hWUk5rOTFVbm81YjFjMmFFdDZZVzAzT1ROUWFWUmlSbTV3UzNaUFVFcDBWVGxSVDBSUWRETncNCk5UZ05Dbmh1TkZsYU1VMXFWVlZEYTFKRVQwWmlZa3h2TnpoUGVFUlZSMEpCZW5sUFoxWlRaUzlKTjFoMVpIWktXWGxETVd0emVHbGkNCmRuZE1SRmxvYkEwS1JFTTJURUZxYlZKNVp6RllVRFJpVmxRMk5XZElSelpCTWpodksxUldLelppWVZrdmVXNXhiV2h0UW1WblpXWmkNCllrbGthRUY0V1ZGVldFb3hEUXB1WVVrMlVWUlhjbWxpVlhrdlJIUXlRME00ZHpWdFNUaEZSamxNZVZkYVQxVndNMkpITkdwQlUwcFoNCloxbFJlR1I0YjJSSkt6ZFZaMk12YkhjTkNsTlhUbTUyWVM5b1VtRnVOVFpQVVZRM1pHcFFORTgyT0dkbFNreDFSa2g1YVROSE1YaEYNClpWTnpXbElyU25reGVGTlphRXRJWjFGamVYUkVTdzBLUkcxQlpEbHBOMU14WVd0alZUTmxVVFpHVjFOclp6bFNNM1pQZVVzelVYTkYNCksyZEVRblpXZEVnMWNHUnhOM1JsYWpndmFtZHNSM3BwT0ROTURRcHRkbnBOVjFwdFQzRlZWRXByZVhGVVlXTk5Nbk41TlRGb1Yxb3INClprUmhLMFJvT0RSQ2JsWjRhMWN4TDNCWmQwZ3JaV2hUV0hKTE1HWnJSRGdOQ25kd2R6aDFSazA0YVZSaWNIWkdiRTlMYmtSVU4wUkYNClVGTjZhSFIzU2pad2NISjBaMlJrWkhCcU9Fb3dXVTV3ZUhoMU9XeHJiVkJLTUV4S2RBMEtWemxSZVRGMVZHMHljbmwzYmpOSVpWb3cNCldGVndZU3QxV1ZWQ09VUnZXSGx2Wm1wa1QyOXJWQ3RJU0N0bWVVWnZUMUphU2tKVlZHRjBVR1E0RFFwNU5rcEJkVFIyVlN0WVpFbFENCllucEljVE5GZGpOdk1IQm5URFUxV214clNDdFFNbUo2ZHpkUk5FbEhTRTVtWlVad1UwcG9TM05XUVV0amJrVU5DbFF5VVZkcmFFNWkNClRrdGxhVGxSWkZkSGJTdHNaM2x5ZDBsMVFUaHdjRlpEU1hKelFYTTJOSFV6YlRreWNtVlFWRGxEVVRVM09GaDJXblpTYXcwS1RHdEwNCkwyMWhObVpZVkROSFZWcFNlSEI1TldwMmRYbzRkVkJaVlhSWWRXeFZVa3hrWkU1NGFFSm1aREpqVTNOS1F6ZDBkME5uY2sxd05tbHANCkRRcG1RaTk0UjNwV2NqRm9kR1p3TjA1aGFtVnNkbUZrWTFKdGRXcGlNSGh1VEV3MFdrdHRlRFp5UW5WMlQzUlBMMk5FZGxOV01tZE0NCmRrOXVXSEFOQ2s1bE1UVTRaa012WkdKT2VHTlpRVWQ2TlVOMmVYaGxhVkJFTVdsc2JXdGliRXhWUWxFeGMxWkNNVzVZYWtOUE5DdEwNCmEycG9ORGhWVVhaWWJnMEtjemw2VDBkVWNtUlVWVVF4U0dGSVFWbzRMMmxZY0RjdlVtTnRZa3hHTDFCTGRXUnZha2RQYWtoSGNHTnANClJtRkpXRzh2WkhsWFUycHVNRk4yRFFwelVrMTViVmxZWlhKclNGTlRWWFpJTVhNd00xbEhlV3BKVUhCelJXUmlOR1pUYkdsT1ltTlQNCkwwOTRSekJYTkVab1RGUmFibEZQUldOMGIzQU5DalJtUWxGNWRUQk9LemhyVmpkTFJtOXVhMHRzUlc5Q1NHZGhLMUp6UVdRNVNteHENCllUTXhXV2MzWkZkaFVsWjFiRkEwYWxWeFN5dDNUbEFyU2cwS1YxaHpTMU4zUkRGNWExZFJNR2xLWmpJMWNFazRVRzlCY1VGM01WTlUNClkycHBUMHh0VDNCSGNtMTZTMngxV2taS2NuZFpiVEJvUkhWTFJHd3ZEUXB0YW1GbGRYb3lWa3gyWTJ0dE5IRlBNbU0yUVRaNmVITnANCmVHaGFaek55THpsdk0yRjRhMnQ0VGpsSldIWXpWa1phUWpKcmRuaDVTVTl2U1VjTkNuWlFVMUY2TDBkWWVreHZhalpHYkcxM2MxcHQNClJVTkNZbXRWUkhOUGIyWkNPVkJ6YzJOV2VGUm1aVEJ6WmxSeE9XMUZiVGxEYmtwMWFtd3pRUTBLVmtwSk4xVkNOSGM0YzFBNVUzSk8NClRUaFViVUl6UkdaMFVrdDVNV2RXZEhjelJYTXZlRFpVVDA1RGVFTndWbmM0Vm5kamFFOUZUWFpYZWxGb0RRcFlNRlkyWlV0elpERkcNClVESnRSRlZPYTFwMFlYVnRkMjFaWkRnMFNVOUpRbmRFUXl0NEx5OWthWEozYkZCMVdFSnpXaXM1UjB4eGMxaHJaM0VOQ2s1SU56Um4NCk0xbEtVME5XV1doU2VXMXZTamRNVFdKNlpHOVhkQ3R3VVc5S2FFTTFUbWQxV2xBMFZXWnFPU3RYYkVGRk0xbFhVa3N3UXpaWmNnMEsNClRqUm9TV3AzSzNSWVdsbHRSVUkzUjBZMGVYbFJhV0pCVUZSRGFTdFdlVkl2T0ZGRVNVUjJNRUphZGl0aVlrTjFXako0Y1Rkdk5DdFANCkszYzREUW80SzFWSk9Vd3JVWFZ3YUdSaGFqTTBWMlpsZFhCcFVIa3ZTVXBpWkZWWWNuQndVMlZZYlVkM09HUkRRakZhYURCa1EwcFkNClUwcFZURmhLTUVnTkNtZDRSMVZaTUZaS1VFRjJhRVJaVjBwaFZFOUhSalpqZFU4MmRFODBLekZyWWxsVkwzUkdZa0YxWm13elIxQTMNClYwRmxjblpCV2xaQ1YzVm5lZzBLTDBZMVpEaExiVnAzTVZwM1JXWmlTRWhoUWxrMGRuTmpOSGwySzNadmJVOTRkVlI1Tm1KNGIzVk4NClRVNHdaVFJLWWtzMlJ6Wk1iM2xCZDBaVURRbzBSMnczTVRWNlJuSklaQzlMWjJkalZ6VnBTV3BoVkRKdmJYQjVaR3BHUkVWNU5qTmoNClQyeFRhREowYzFweVlUVnZVVWx1TVdKVlNWSnlZMFVOQ2pCa1dXaFVkMmh0YURWTE0yMUhVbXNyYVdwa1RXUnBjMDFNVUZSSWEzSjINCk4wSldZMDExYjBFMldESnNOamxCTHpOTVVIaFpjMUYzY2s5dFZnMEtkV2xvT1RocU1XUnNlbkJpTUV0TlRVWkVaMk5FWW01NlppdGwNCldtRmllVVIyV2xOMmEyTXdPV1ZUUlVWb2MyRTRTVzQyVDNnNU1VeHdiVll5RFFwUVUxaENlWE4xYTFSYWNIQlVkbGhoWTNod1lWZFUNCloweFlNblpSTlVWQ1NDdDJiRU16UnpSclVsUnJMMFowVm14VFR6SlJXVXhNV1hNMGVsVU5Da3B6VG5OcFlYbzRha1FyYkZObGJGQXgNCmVteHhVekZEZEVZdlVHdDBjRWhEVHk5YUswMDNXVzAwTjNGMmVIVnlhbGxwY0dkeWVUbFpiRzFWY1EwS016UTFPREpGT0dwWU0yWngNClJYWkZSalU1U1hScWRtRm5lbFJsVGxkYVlWTlhZVVJSTUd4UGN5dElVRGxrU2pGcWQyYzRSbXhUVmpNME9GRnJEUXBKTDNkWFNXZGwNCmNXdHlUSEZRTkV4WVRXdzJPVGRGZG1WdEsyeFNRM0pDUTJjck5sWnhUalEzZUdKWVpGaE9iMk5DYkRCUWVWRkxNWFowYWs0TkNrTXINCk1scGlVMFZVYnpsdU0zSnRPVGRQTVhwYVIyTjVlbUpITkhGaFZqWkNRMjFMVlRSRVluUU5DajF5YVd0bURRb3RMUzB0TFVWT1JDQlENClIxQWdUVVZUVTBGSFJTMHRMUzB0RFFvPQ0KLS0tLS0tc2luaWthZWwtPz1fMS0xNjcwOTM2NTE0OTYzMC4xODI0ODUxMzA3OTQ0ODExNC0tDQo=", + "historyId": "1380609", + "internalDate": "1670936515000" + } +} \ No newline at end of file diff --git a/test/source/mock/google/exported-messages/message-export-1850f9608240f758.json b/test/source/mock/google/exported-messages/message-export-1850f9608240f758.json new file mode 100644 index 00000000000..6d15f2c871d --- /dev/null +++ b/test/source/mock/google/exported-messages/message-export-1850f9608240f758.json @@ -0,0 +1,150 @@ +{ + "acctEmail": "flowcrypt.compatibility@gmail.com", + "full": { + "id": "1850f9608240f758", + "threadId": "1850f9608240f758", + "labelIds": [ + "Label_15", + "SENT", + "INBOX" + ], + "snippet": "", + "payload": { + "partId": "", + "mimeType": "multipart/encrypted", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "multipart/encrypted; protocol=\"application/pgp-encrypted\"; boundary=\"----sinikael-?=_1-16710037670570.7713496225282256\"" + }, + { + "name": "Openpgp", + "value": "id=E8F0517BA6D7DAB6081C96E4ADAC279C95093207" + }, + { + "name": "From", + "value": "sender@domain.com" + }, + { + "name": "To", + "value": "flowcrypt.compatibility@gmail.com" + }, + { + "name": "Subject", + "value": "Test message with base64 image" + }, + { + "name": "Date", + "value": "Tue, 13 Dec 2022 23:42:48 -0800" + }, + { + "name": "MIME-Version", + "value": "1.0" + } + ], + "body": { + "size": 0 + }, + "parts": [ + { + "partId": "0", + "mimeType": "application/pgp-encrypted", + "filename": "", + "headers": [ + { + "name": "Content-Type", + "value": "application/pgp-encrypted; name=" + }, + { + "name": "Content-Description", + "value": "PGP/MIME version identification" + }, + { + "name": "Content-Disposition", + "value": "attachment" + }, + { + "name": "X-Attachment-Id", + "value": "f_nSoDBUiHcxekQPLTIPVJzujYHPnBzo@flowcrypt" + }, + { + "name": "Content-Id", + "value": "" + }, + { + "name": "Content-Transfer-Encoding", + "value": "base64" + } + ], + "body": { + "attachmentId": "ANGjdJ_e6kiXztgDZkBl2Obd15fJGBVWuduR-Zvaq96S6s4808EHKO21xhSoHRZCme_KTT6olV-yDz3RAvdm9NNvNHWGJRlWD3fmNUF62sbrBrdW0UJPZkJ6qTo-nfu6EtuEr91yITrz5JF_7EDfYweWNda6gqSHygzn2ewOQc6AALKabh04HAI25X0EjmaLfpcIxjs2ggXZtotNvpq2MbXsu5VWUL_if_3GJy3BsBHxxeGykqPNsPdSxYCKvv6moltKor8j6jukaMoTMhJuic9-r0_J_wKE7IrOk6VguR7hjfw9DN3bIlmrtUXFzcmKv1oUsVhzuK7l-7VbJal6oCo8wYHKD1Nky2fi3ROqE0nGLAfUHR5vbiXI9kLHwMDfaz9xsbb-oMKdg43vnuih", + "size": 10 + } + }, + { + "partId": "1", + "mimeType": "application/octet-stream", + "filename": "encrypted.asc", + "headers": [ + { + "name": "Content-Type", + "value": "application/octet-stream; name=encrypted.asc" + }, + { + "name": "Content-Description", + "value": "OpenPGP encrypted message" + }, + { + "name": "Content-Disposition", + "value": "inline; filename=encrypted.asc" + }, + { + "name": "X-Attachment-Id", + "value": "f_aRWoPtBoDpOUqzQhTUjNYoPiKRZOgV@flowcrypt" + }, + { + "name": "Content-Id", + "value": "" + }, + { + "name": "Content-Transfer-Encoding", + "value": "base64" + } + ], + "body": { + "attachmentId": "ANGjdJ-kGspPnPk13mtXbRyIBO-IfsdJOi9PXPdz-bWfd8-2fkbysFXjcRukv3YAISC4KbsUN2hME-vLTK9Uai865cRg2pXqD_Fxj-ywnkjG70v_tSo_KiB-yTR6TdevcGkW-7CMthTDFSPhJmS0TgNjqOPrpC9NSk-UEVqYh0s7zLZ1IvCipkJeDZZZwOyFdDjxZZyRJaO1_x7ipb5hbZ59iW8nE5wbZ_DBUAuDyDtgym9swpurufUkJFlZ4rpNdO8sTqDHcueGz6kf0Ns2g6r9yDfVIG_3RKtMGTRjGrkPgHkSk4_biTHJh0J6D6-4VQCUA1A-i-18nKkeRlrw9iDk7bdfoKcHOcjdwxXkhBzJPqdOb1noD1MvZdyOKoU2LHIkb6PDbfCozZf1w7p7", + "size": 17118 + } + } + ] + }, + "sizeEstimate": 24840, + "historyId": "1381250", + "internalDate": "1671003768000" + }, + "attachments": { + "ANGjdJ_e6kiXztgDZkBl2Obd15fJGBVWuduR-Zvaq96S6s4808EHKO21xhSoHRZCme_KTT6olV-yDz3RAvdm9NNvNHWGJRlWD3fmNUF62sbrBrdW0UJPZkJ6qTo-nfu6EtuEr91yITrz5JF_7EDfYweWNda6gqSHygzn2ewOQc6AALKabh04HAI25X0EjmaLfpcIxjs2ggXZtotNvpq2MbXsu5VWUL_if_3GJy3BsBHxxeGykqPNsPdSxYCKvv6moltKor8j6jukaMoTMhJuic9-r0_J_wKE7IrOk6VguR7hjfw9DN3bIlmrtUXFzcmKv1oUsVhzuK7l-7VbJal6oCo8wYHKD1Nky2fi3ROqE0nGLAfUHR5vbiXI9kLHwMDfaz9xsbb-oMKdg43vnuih": { + "data": "VmVyc2lvbjogMQ", + "size": 10 + }, + "ANGjdJ-kGspPnPk13mtXbRyIBO-IfsdJOi9PXPdz-bWfd8-2fkbysFXjcRukv3YAISC4KbsUN2hME-vLTK9Uai865cRg2pXqD_Fxj-ywnkjG70v_tSo_KiB-yTR6TdevcGkW-7CMthTDFSPhJmS0TgNjqOPrpC9NSk-UEVqYh0s7zLZ1IvCipkJeDZZZwOyFdDjxZZyRJaO1_x7ipb5hbZ59iW8nE5wbZ_DBUAuDyDtgym9swpurufUkJFlZ4rpNdO8sTqDHcueGz6kf0Ns2g6r9yDfVIG_3RKtMGTRjGrkPgHkSk4_biTHJh0J6D6-4VQCUA1A-i-18nKkeRlrw9iDk7bdfoKcHOcjdwxXkhBzJPqdOb1noD1MvZdyOKoU2LHIkb6PDbfCozZf1w7p7": { + "data": "LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tDQpWZXJzaW9uOiBGbG93Q3J5cHQgRW1haWwgRW5jcnlwdGlvbiA4LjMuOA0KQ29tbWVudDogU2VhbWxlc3NseSBzZW5kIGFuZCByZWNlaXZlIGVuY3J5cHRlZCBlbWFpbA0KDQp3VjREYWZTVExlUXYrbFVTQVFkQVlDeU9uSFdaMFJlMmhQTjhsRnpCTEpNS1ltdWZLQU0yZGhJWVpKMk4NClgwRXd4UlNScFJKblo2U1FDb0JXMnFMZEpzSmlnZXdvL2JVSERDdkVacXhWZzRTdUVEVThYRGtnZmZ3ZA0KMEtlZ09ITUN3Y0ZNQTB0YUwvem1MWlVCQVEvK0pGRVhGTWtkTUJNNmlUREJ1MlczeG5GKzdhKy9zTEdPDQpLUTk4UFVTZ05zUENTWnNmUjFHekhTdXVpYlQ2SHVDMG9KRFRGWk9lMUZ2clV5YlpYNXpaMnd2OU1QT3UNCjFGRjZIcStGcEZncTZzeHc5WTJ0eXFrdEVCTTRhQUxRWDVMcGNqZGpjcEVETmphMkIvZFZiK05tTXcwRg0KSlZjc29aby9FUU9mU09QWmZFZmZrbktWbGJzYWVUdWFsTm5LQlVvMU5qVEtkdkpEenNMSjFKc1kwcnhlDQpXWFVhd0Y4ZFplc2xNR0g1R1hYb0g2dWlCaldKeVBPUGlIMmZualhtWFl4MWdnelorSVRERllWZlFWMGsNCjJyREFtb2xoT0thVlJXQXZ0M2g1a3B6VXIvaU5mOWNmTGpGY1ZoV1FFZEJYM3psTmY5d0E2bmZvemY4Rg0Ka21EZldwZVBkaXdmbU1KYVBTYkVnM2RRcGQveTd5MGovTlNGUGk2MXM0cVI2TWR1SWpzQytHM3BISGw0DQpkeUNQOG1KRHhCMjUxbkVzVCt0VUtIVTRHaWZWTmFEZlROa1g5VFhrSXAzdU00NHhKeFlsMkMwR2lBOEQNCnBZWi9MYlhNdHdRNEt1MVFQblpMbWlTRmI5TlJSVFZ5ak1meGRQcFZHcnpXMjhwVTdsMDBDYzV6cEhPMA0KRWNUM1FYOEF5NWJpek9ua0lHbzRTQXBlMGVoYzlqZHNkMlQ4Q1BReC82ajV4ZzdDVENEYjZNamhibDRIDQp5alkwYUhmYSswSzJMQjlWUjZnVEFGNXl3dDgwSUdRbTRtNWxtc2ZwaGVycS9xU3I4UytDV3AvOU5mdFINCk5EM1FHVDlRZDZNMzZlMDF1UWJBaVU4dWREdklLK3AxOWZyWURhWVlMeElZY0ZSamZUWEJ3VXdEdmIxMg0KWVBhWmpjUUJEL3NIemo2YlJQOE9nUHJCbmdueGxuNDZOMzVEaGlLSG5FMFFIaFA0aWQwNGlWaEFHS2hxDQpuOVArdW14dlBmUmlMYjdDMHQreDlzcHZuek0vbG9jN2pFRWFxQ1NmVWlRak9KclBFUFpLNjZ4VEFOVlINClJIVHVuWnpjUWIrbTY2MzN3V3hMdFR4VDYvRUVRcHN2NDFBN3BEaEo2Tk9DQUhTUjNrSW5RUnZoMi96YQ0KRDF3UFpwUmRVakh4bE16QWRMTnhiRTU5WmZsdmZXK2ZKVW00V0JXMjE1RGZURHB4ZWQ2WXdzZFBqbE91DQpaT3FKNVQyVHY1VTRsOW03bkJlYytNQXVQZ25xUnVBbnFaQ1ZuZ0Npd0kxYlRBY3QwaSthblJzZEZMRUsNClU1a0pvdnJVS0R3STlyVm1rSHdpZ2ErTFRpMW9USFBGTjlDTnBGMlpuL21ZNmZlZ1Z4Zmd5WTBkazJOOQ0KaWZ3SURyaUVZcUw4YlJ6MHdwNzZMK2drN3o5b1k3ZjdLYXBxdmR4Z050Uld1ZHpycEZLZ0VZZ1Q3d3UzDQoyRnk1ZXByUnVmSGZxRU1wS0NwWFhBYUhqb3I0ZTV0Mi9nVTFsNTNabkhNSVZpMWdnTVR4NlRyWFRlalcNCkZ0MXJOdUhqaEtkOENqTktDS1V1T2EwMCtxRng0TjkzRHZwK1BEazk2aVk2L1RXNDB1Y0kvc0dRaDl4Wg0KRGNMekhKWkZYd1lWSEgydHFQejhyaHdmbERLYUhxdzBBSHhzVzlyWmIrbDY5eGE5VUd4Q0JwcTM5REdFDQpUL0xRTkRtVUtLYXVoNTZFMVRoMVk0cm5aQjV0bDRHakVMR1pHRGRTOFJwS3NwY2FmQzh1V0VCUVQ0UEoNCmo0UTlqWDNTSVhkQ1M5U29QQk5Jc1ZpWkMxanNHdkFORXNIQlRBTkxXaS84NWkyVkFRRVAvUmN3Rml0Zw0KNmNxYmRQS2hBUm5qN1FsVkp5bGl2N2FsVWR1UE5KN2VhL2ZkWjF2VzFka2cwNTRWbGl0YkJPWXdNL2VSDQpZRkgyeE1nVVZ0N2NMTjNobXlFSmJ3MnQxd1NrSUp0VmxNRGRQWUVHTHZybTlwQUhia3ZJTDg1eWVnWVgNCnl3RTVHVEpJNE0zdjlKQlBabVBDU2dUS0FKUVZTU09ldndjQ29RRGtha0JEbEhpaTVoZmhjRXpnMFZGUw0KUGgxRnR5SDB5OUNyWUZQOWFyUS9BM2wyUTBYcHJnbThiNUFJR0NsUEpvZDQ5dkNtQnFKb2I1b2VZQTNyDQpqU1BWa21lbDUwVUlCYVZOOUp0L1pnYjljdlB5YmU4MVFjb0c3Ly8wVzJ6ZEcyTHZNWlZDNXl2YmJiajENCjhkK09jNjFMSTJzY0hrVnFQSjBMcGtuYnNObGVIUWR1ZkMvTHhTM1RQVHpkdTRwTEdGZ0dvRHI1NmoxUw0KQXdPT0hQanFFU05RVUV6aHQ5amdNVDE0dDlEdVNHZm12NDdMMlozdlJJNDRuU2ZtM25Jc29RV3NPdjFsDQo2aDNIVlJBMDZZLytINHNneW1RbXdvMGg1UEljNWVKTlJjaFpOZENaSHI3blpOYkprYkxBWXNzVi9NY0QNCmhUZFRiT1F4UnkwQm5kTnhkR0htVkdnNHFGVlJob3ZNbG5ZQ045OXdEaityUVAzQ0JrQzFwTk9kT2xxQw0KQW9qVG1SVVNQSmFDRGVtdk9URFk0OEY0c1A4WE1GTGk5dGEwNlNaRGZVSmVqc0s5dTAvVkxjbS9PalpzDQplZnU2cFN5OEc4ckJBK3ZpZkZUcnk2NFd6ZnRMUlEzSVFTYmtRL25seUlCNnpQV1FNbnVweGV2a1dsM0sNCkJCK0I3RXZFZWFxZ1l6Ykh3Y0ZNQTcyOWRtRDJtWTNFQVEvL2ZQMHdrU2lxYnNUSGxFaThaaWUrb3c3Qw0KRnVBT2lVbHVjbkZlK25FVi9SL2hzT2wwSTQ0dVRmUGNZY0RVVHpoOHpyeGdFYWh0ZEpZRlBKOE5IYW1hDQpYc2pMTUNjdkdLcTc0emlhdHJ0Q0pkbXF6SjJMTzZIaVRaT3ZUSnhySUUyQVhoZWN5VUN0Qno3cTB1N2gNCnVrNXViVEthUUptTGp1MVFYeDNTbnNmMTk4Y1owUGlQTDhuMjNRRlVCWTg5REJBR2FCNGMzaG5vdzdFOQ0KcHNGWHZ5ak1BVTdVcFNpQWxpSk9salpmV3dnYkNoeHVQWXdMR3hCVUtEWlJiME0wYk14RWMyWW9NT1Z1DQpBVi9ubTI1aHJoZmhnbXBNMUQ4b2lxeVpYdWpleUpZemd5anBYTko2SXF3ektUaUc2R1pHU0oxQVg1S3gNCmxBNnJTdDdkQWZBbHk2M2ZoYWcrUVhTWHJlbmRid0lpRVVmYmw3MmdhSlRtNkVNbk53VnhzT3BzdTQ3Nw0KZy9nbG5VRnlVNEY3N042eWpJK2JVZTZjb0MxWnlqTTV5VTUzNTVlaE9wY3hsT0kyTVJwUDFxZnk3SVZwDQpNYk9JZGtMTk5jT1h6Q3FuOVdxSFRqUUVaOENWMS9qK3l1QTRnTjI3NzFPU3l1Qmk3T1BqUGdFMXJ3cS8NCk8xNjhZVHBSY3lMRHBoREppTklIMVlnVUdIdWxMWFZ4YUpBMzI5UjZJR1I2TXFUTG1sQVVpR3JoeW9KcQ0KdGsrNldVQlBMUEkrYkx0NTZ6UnlOL2RjZ0hNb3RmNEJUNVZZRldmRkd3Z1JXdjBDc0hmanN2M25EYUZBDQpBdDRuTkVIUnY3OUNMNHF2TFB5ZS9BQ1phT3djMkRvUHhIc2lSTjIzSnNtUGx6MFV1Ym5aYm5ndVR5WFMNCi93QUFKM0FCZVpJT2IvZjl6M2MrZzNzNHNHQkFFRUpwWG12d0xwd3NBU25xVlNURUE1OU9UVWN0Q0dNNQ0KTzZ1Mzd2dEVWaTZoSEY2V1JPRzNLTnliMHY2ZWZ5NSt2ZEsxU01kYWdrRldpTmxHRGtnM0grOVFzL3F2DQp0cnFCNzhLUFV2cTREYVl4Zmhkcy9taXZGR2xER1Z2NWU2dk8zblVSSm5sbU1xRWxZQWlGVTVhWitVT1INCkhxRkxzVmZqbTFqTW5HOVhBeHVKRDlWd1h0QUNDNW1tZnB0MWZQOHpKWXhkMCtRTngzNyswNVBtRjlEWg0KeE1WakhHb1M5SnFHcVByOUdBNzV6eVJhNmtxaU96R1RJd0lPTm5NaXJRd3ZzM1NvZDJtMnZLQU5ZU1hWDQoxVzNOaXk1UmdLMWpkWFAxMEtQUzVPUEFQdzdrN2VmQzlOS05rRnZZaEtTSVlGT2VSempZUjlqMkJqaXUNCnVXdzFTZUJqcCtjSWdvbkRSeTIybldsMEpLSkdzT0ErWit3SEcyWnJlZUJIWXRocWV3eDdvV2dBRE8wVg0KVzhOMzVsNjBWTURvVlpiRENtMnZ4dW9qT2NwMHNIYmphOW01RFVZanFvdzVpSVFVM3VQZ1BhNzlMOFE4DQpEV2hRZk45bjBVVmwyakZLTkNjZXVwU0tNNE92dXpXWDhHSFNjaGE3UTBOK1BBejNWM0g2MlRDLzZGaVQNCko4dXRyb0p3cWsvcExDNlpJMFVGWUQvdmRoRTAzajVnN1hWUG5BRStRUXVEem9XMThjOFFBbEUxbVR4VQ0KNXRUSlhXU1hNNlRGaW92MGprd1JTdEtVS0xrcFNEak9Pa0RYL25yaUR2VFR4cHBmMjJKRVcxWGlBZTE0DQpIZjdsQmJVNDdKZ29nNVVnNDJoaC85RDRFTHJoN0J3SEdreW5ENzNzRnArMmM1c29xN3UvajdCM2FGTngNCmNEQ2JuaGlkNjFhRWFVYVVXWGJJVUFGa3M1cGxJSk9NYXVSZmwwbUlKb0t5SFV5aVdqdDBDMTc3RGdMWA0KaVVxenB3dW04cEJlc0lPcWt0WXd0K2J4bzY4TXowRTMyV3B5cS9uSjlkSm15QURDRk1BNS9Tc1p4SFBxDQpsMFZRU0cvenVVdFdvK09ERWJ3NW5HUSsreVJEVmxWODFTdDNGeFE2eW1yZ3pVaGlpNWo1VWNaYnhXYmoNCmw2OXozRndKK3hmNDc3OFJtdjdCZXBTNHQ3d05aakpCVGI0MkhBWE1KQUpBQ2ZMOGplU2R1bU5NM1lqYQ0KM0hNSzRZSlhaeXVFSEg2NFR5UERoV3M1cnJ1SjhJcU5MTW10dXBkSWIybVFyb3ZyL1dnMVNCMjlNYjRGDQp5WGF1WUN2NS8wMVFjN1JDcVNRVmJKaXZlK005aXVmNmVoV2VNcWdmTTFFWmxHNHFxWHh6SWlDbUZoVisNCjVTVndsdW9PYjhmWHlabWZYY21DTlNrNVFmN01TVjh0UFJ3aXBDTE1vVFpOVERnbVA2RGljZGN0L01ycQ0KdXlTb0FXYTEzV0d2ZDhzRXU1ZTVlaXAxZHc4Q1d4dDVRNUNRKzBkNURwM3VRcmNEOElyZmxaUFdzWHVIDQp6WmF6TWFGT1k0Y1l3d0p1OWF0K2lLRnpwRjlSSFliSTUxNFk1Q1Y4a3dpRGE4eGt6OUdJMC9KTWdDOTQNCjZNZVVwOGhXYjJNVnNGMEhCc1VhQ2thMFdqdms0UlFleFJiR1NVMWdRZFlJVmhMZlpRaXRVMzJ0MFVxVw0KVXhsRDdWWUJWN1BBbHRqTXpCS3MwdkZNY2R4L3I4VSsxRlVuM2g3MmIyMHUzVmFiNXFZSE9HMUtJWFJ3DQpWOVZoNmQ3eEFEVEFjMnFPeEl1bHQraHU4RGg1d2o1NElPM25qcG9zSUl3UDRxcEowWitFbGlWaXVodWYNCnVqUlhEUWhweGQxcGIyM001N25ObWdsWVVJK3pGVkhhc1JzNy9sS2FwdHA4RWtqQU0wdFFsY2tQaW15Vg0KdVV6cDFWQk52eFVSZlp0T0RLRCtudVB1UWRMQkFoa1BtdU83TWoyQzdpbVFsUjBLRnA1Rm5GSTZIbU9mDQpIRHhKN3pLeXNPb1ByS0FFU25KbzJIYkkrMG1ib2dTdmw5VnBBcCsyNUlMTHU0WUVycmJ2bE9DcXFEeGYNClIwQ3J0aktNNkQvRVV0U0FibFh2c2ZhdDkxcEp5NmlpYkJnSnk0bzlsWk1abkNzcFRFWkc2bWxRWnBJcw0KaUI3UTN5eGFlb3BuZFE0SzZqZk5xVm9KaHc3cHBzdlNHdjIyOGxHdkhIM1FFVXVMWHpUNU1uVGRXQUkzDQpGdGtyYVpLSGl3d0NjUTNURTlnZ2Vtc0Z2TTNjMEdpRWFYaW9FcDcxcWFJWndNUGV4NUlaQ01JSnUwNmwNCm1ETGgrRm84dENsVU5NbWJ5Q2VVNWRDMnRONXFxUDJPSEs0OHJmT3M3YTl0WVBqbm9yNWRxb1NlV0FlVg0KcjFqN0g3YXlTQVNndWpLWGpjVVQrRVE1YWlzd2lPd2Y2RzVtcTZaNWU2NUpocnhWOFlyRVNvVVZLaDQ5DQozcXVxSlZvQWc1a1BBaFkxcjB5cmJBTWhFOW9Wc3ZXclF3dHZPT01HczBraTBMSytJMmI1elIvNEM5TjANCjBCT1ZTWTVBRFU3UHZrZjJFQkk1K3dhN2Rmb1Y1K1VmQ3hzUktuZzlUTndMU3JicVhiVnlwYXdpZUUzSg0KdmFXaUZQY0tyeitla2xtOXA5QjhFZXVRQ3EyTEF5bGlvR2tqN21nMDhCYjdMeHdGbTNUNjVleXl5dFJDDQo3WjFkSFJyOHIvaWpsZ05ZL241ZFllNmRmRTJJdWsrQnA5VUZidVZDUTdwM24wL2Y1akRHbDg4RXRrWFQNCkRMTjQ2QXBqcW9IVGJGYXIyN3JJRlc5bzVzZHNPWkRXeEJaUTRPWFhlWGlFb2YyNjFNeklvbFNwVjFPZg0KdmI2bXZVVjB4ai84U1FNS1JTc1F6elg4TFV1bk1PQ0FNTG5xZmE2eEZtL3ZidW5YSTN0THJMQ1JlSlF3DQpickcvY293TmhCeVRIeXdGQ1NSc0hlT2xycTBSbHBMQnZFS2ZNcUN1ZjA5bVg2VFZ0UENSWm5OM3hHTDYNCnJOTWxPa3ZKR05qaWxDSC9DSURrNkxzZEsreTIyRmtQaHZvVjJobmd1WlJsYlg2MFRpT0ZwUkJYQTdQSQ0KZUl0UklDYjZuOHM4MG1WUENpMDNPdFlPYm9DcXFYWHY2eDJJclg5Wk5QTzFHelJyY3JrQlI1QVM5OFYzDQpvQ1cvNk5zVEMwS0ZGdzNyUGpmWTY5YVVpdnpzOHhtbXdzWWdsb0ErT0xGdTlnMzVBbmdyZ0FhL25kWlENCm94WVlrK2JTTDgyaWVzdG5HMHFsZ2VLNmFyUWxjSzhWWnVTcFlkWTl4eUFRNmE0aEFqTHdlQVNSSmF2SA0KdFRLRmFsZjdwNXNQc2xzSVlmQlhicDhkQjJvV0VBNSttWHJSNEh3NW9LdGRkT3BzUnFVcnIwWDNiSnh2DQpFSjdObmp4TDJlcEY0U05tNG90SkxHNHRPOERUZVBnVmJIS0pPVEIzVTFtT3pIRzk1emNkSmZGMTIzbGINCjF4K2plOGNZbysvMmJGcXZTNUZRdjJYUWpjakNEZ0tJK1pkSGlVSjhmS2UxU2g2bHZCNXRxWkJrUU5DVg0KdWFRQnpUZEFjcWp1VEQ2VzhMQzA5Wm96c1lSdFdqSTJnTVNQNEt3Q3ZERVUxL2szTTlleVh2OU5Hek5uDQpJYW8reHdLWUtWRFRHWklRa2J4QXFQbzFCVXRHSUE4NVgrYmo5dDlXaTVYbUp5UmY0SDdTTi9Vc3pjeGsNCmpZT096czRWT0JpTGFaSGJ3VUtqSzRiZWtxS21oRnNPNWV0UTIxeDlGMWJ3NmczSjVQWUlFUXhBa1JrMQ0KS2ZCK2luMkJXaldmVHFUR1laZGpwbVdsNUpmYnpsenN3Q1NEeWNlRkRERE9Kem1zWE56Q2FUdTQ1ZU9BDQpvV0Z2ZXFnOXlOSGdlM2FyVG1aOEZmL3hjL1JpU0cvN0toWkxhcFpPaG5JT1h2c3cxNGJUeTYyMmlYK2YNClBUM2lxb1VHOEhQTG5SWFd3TnlEb1NtV1lqZjRMMXI3b2svVDVGNnNZWGFndWpUNC9ScW5Odzh0VlV4cw0KV2RCeGtUR1dPb09iUE9zOUZtdFRndVVnczJ3SjF6ajVtcUdXU3BRN1ZPbXpENzNWWEVtK0FCNll0a3NEDQo4a2ZEZEF2VTVLYU9FaHhpb0FIb0VUNUw1TmtlWkRhVzJjNTd2M3ZKSGNQS0ZvWVduc2Q0SXlXTjJEODgNCmIvT284cnZtTkEyS0sxK1o5Z1FoVUxUdWhCTTlyNS96K2FCQ2paUU9VVFRFWHF3UTFXZDllWEdzWnRSKw0KWTNTUm5RYTN4OG81ZnZHR2gwU1A3ZjQ2UVhRclZIYjhTQTlIWDFlZkxoNVVWekNRa2VZREczblRrQzBLDQoxMkxvVmdkSURwMC8wM3UxK3poWVpHaW9FVjJZN0lwbFpoOFVMdEV4RWtleFhSTDJWK0M0Nloyc0g1RUcNCllobWh4RkN4a3poUjhyUFJMcXZVRTVqelpIc0xjUnF3L2VSeEZHNTJJNlMzb0pPTmlHOTk2Yklkb0ZZbw0KU2V0V2s0c084U0EyK3pTd3NmUGdFend1V3RWTDhVVkNSSzQ3bGFUeS9vMDhzNis5RXlYSzEyL1NmU3gyDQpQMGFBRDgzcE9SMldpMlU3bEFVNXRoWUx3Ry9nNytLSmZsMXZWbVBDSDF1bDNSTEJyMituLzQvb3JwYUwNClhmbWVnWSs5eHRhZXdOZVJMZ2kwUjMvcmJRdUg1b0pGN3E2M0NMT2c3RGVIcFZDZGhwdDFtNkxiTE10RQ0KYTA4VTlNRVJXcUllVkJVZkhpVlJMYlhkUWd5UngvWkhLcFc3WnVXQWY2WFh5RDd5dGd0ZFBnRDdEQ2hVDQpUOUlGa25TK240SmJnLzk1NS9vZ01kQW4zcDZPc3AwcWY4dEQ2MzVVZmFmTzRMa0ZiTXdjdVlSYmJVRnENCkZGUWxYaDVvWXJFVlg3NVFnQU9vT01BWTFSTGtxejl6Ky9uc0xET1d5OTY2cWlGODdLYkZicTJ6aVYweQ0KTkJ4b2F3bFpPR2RkNEp1cVJiK0VGaVY5Ty83NXR3WWt3YlNmN003V0dEQmY0TGNTSzNTa0I1a3BzR09IDQpueTRHRWkxcmwyUzZoTkNLUjNKUjJTNFdEUm41VExhdjB3eXZRUjlJUEZrT1BmT0c5VFlpcjZXZHNXODYNCmUzRHR1bWI2ZW44ME9qOGhHR2dQaGN5elA3clVSUm5JeGdubXZBZDZ3ODVPcG5wMXhRbE5GOCtVbFZVWg0KL00yei9HQWpOaGxBTk9UMnRET2RSbnZsa282MTlON3EweTI3UHRQRjVUZ0Z5dEJVTGw5dlhNNm1ORndHDQpQOU1MSDFBbW16YUVhTVpCb1lTbXVsNEFhUTFFOFBaVk5PS3piMmVocjdnWE9ieWVzV1pHMGFkclVmcjUNCmx4T1lqVlZzNjBaVkxsZVNkZDRMZlFiQU5NUFl6blpyejgvYU91dUlSdmtqMk1SN2pqdkpOMmpFK0dMVA0KeGVXSUFoeXZscVAvQ1VLYW1xM2dEWUE3aEZOL0dvd0ltNFNFc2hCWm4xUVY1bTZtbzd3Y0s4U1pzaDdNDQpham01UEgyWjV0YzRzdmRoMWZDR3ZkSGZHM2d5QnRmWkp0bTRIV3hHT3k3ZTZ0bjNjREE0ZXJzeTFDZ2sNCitTS3JYdDkvMmV4TWlGUzRDcENSclhnMytIZUJFaU5UZ2lUNFZmajY3T2hlTmc3RmRIWmNoeGdGVjNSQg0KWGJJeEJEUDBXOHVwaE1OdzJWVkdoTjJaeHJDQkRhQ2VKdXF5YTgzaXBJbEhHYi9wNXZVRjZvQ0lOT0RkDQpycG5KZWRZaWpmT2dKYi9YUGlOK1g1VFNhWWdhMzFianl2bzNUZ0xPUlRzUDNiaW55ZkNraTVxUnRZU1oNClVmRWpoVDZQSVI3SkhLWU5TUGhDalN2TmQ5ZEdQMlEzSHdsY3BKRjZFRXJ2cnNWcm5PdG55UlZKNDlHTg0KRVVaUFRQcCs4ajZKelpTNnd6U0hWVUROSGU1QmE5ZDlZdDNmc2Y4dFZpZU1RUTg1OWVPMVFNUTFTUVlwDQp0M045SGtYa3J6RGR3UmVrSFVkYUJXUVpuKzdSaFY4aHdDdnVTRm8wYXBTUVRVVnVxeTc5RUN4RHZoSDgNCk5jQUp6cGhIbDM2YkVaaCtiZ0VBQ0pFRjRuMGpEdWR6SjJHY3hjZVdScUJHOW1LTlN3SG53dFpnNmo3Sw0KZituNVFBamJzV1BMWm50Z0JmMHJJSENFMDZBZHByWm1DOUFBZlpUUy9HUjdFN2hqdkswemYwVDZOS295DQpOL1FZYXQrNnNaMEpWLzliVHVIeUR2NUJyblJXRnp0dmovN1JwVXdQWlBvSVRyaUF1L2gwZDlwQkFHbFYNCjdHWUt3M3pSYjd0cmxIcFVPQ0gyZTBmYnhZcWFyNURVeVk5ekJzRkRZUkY4RE8vYW1uOFdrbmFlUmdnTQ0KbFM0dllDYStwRVMwRWJHeHoyOGpRVWx3YnJ5UEgwbWZ5OHpyUjlNcUJTUFYweHl2Ykw1b09uajROSnp1DQpaaXQ3MkdkREZuVDRlUTM2Tmx6QVN5NXdxYU9iK2tJYTFidG84THNxWVEwdjhXS2ltS0FDdE8weUdTclENCmZ3NS90V08xVWZiZlRsZVZ3cGdyNGQ4TEtpSGZub1EwR3puVW9Iakl2RkJodkpGdzZDSEpzVHNzL050OA0KSlNvMlFZS0Z4UktBN2JCeDlSYnJtclZyNEhyQ3JGYlladDJHSTZuRzc1ZWR0OXFPcmVaM1BhWjFIamM2DQpBS0RtNHZBT1lqR2Y4UVc0OC80czc2YTY4Q05VekVDY01SMXMzMFRYdmlGc090NnVxcW04MTdQZXZhR1YNCnJwaWQzUE54cDMrc2drdFhPYU5rQ3Ftbk10MjFDcU1wYVNYb3NNVVZrWXZhcDNvWHNwdjU5bzFid0c3aw0KT1R1ZTRuYlBDb3BGQUFtSzhCYlhBZ0hKSDIrbmx4NnlPL3pldXFNSm8yWDVER002YmlYd2VLZHNST0NqDQpqUk5XYzJHQzJ5T1BreU44VThxa1lkOGwyTnFDWTlBMnhrWkRBUTczLy92WUF5UDl4b3hiVEJVbGJnaUYNCnBtMGcrY1BVWEwvalY3OGdFM2YxTDNOU0pSbktDYnhESWkwUU1VN3gxdHZFMHl1YzUwMlhLYzNqVjliTg0KUmduei9pVVJOSCtsQzJFV1BCdzJPcE4rOHJWU3RUU2VRbVBEZHROaGxZUDQvTVFwRUJqRXpEOXZZZSs3DQpnTTE1bTJ6ZnJYSTdaMDEyVUdwRjltQnJCK2V6dHF0SGxLWHd5R3FaNlhrd29UNXovVUloTjhjNnVJbEkNCnYwNmNDMmlIZ1Y3WGFFSi9iRkZzUTVPbnozR3dhTVc3dmlOUGJXbnlwUko1TlBabFpFa2hHUk04UDJhVA0KOWhvQ3Uya1VNbXFpM25ZMDMxMmk2eWJTNWl4VmVWekovdnpBL2t3K3VtbWs4Mk90RUR0cTAvaFIvTk1EDQo3enpCWG45NjFKUkF6Yk53YkUxaXg1ckxqVklVWUQwN0hzWFFpS1Bxcm5Odi9CTFdSclZEM0l1enJ2eDMNCkJDR2lSNXdycVEwSXdycDZEZVJUb2c1M3RvVU9kNnJXNDk0YUNWNmlpR285eFkwWnZzclUyQ0UxbFpaKw0KOGhmcEFCdGpoQ0szU2JOZW1zbGV3UDZMN2x6ODJBaUc0dHVHY1ZjSW5aMHhLRVM2UnJaTTl0a0l3eFhxDQpGWGxST0FESmdjdXh2anVOSmd4b0QwRVF6NS9BczNPalBqNDgyNEliRHdnaGlxRTZJajI5b2JlU0xlNEwNCmE1bDlHRnREbTdXYlFuL3JUY1hCVDRIYk9HMUJIWjhRYTVJcEVpeE5GcTdmSUJqeWxESEM3SUVHL0JaWQ0KQldKOGQ4S3NuZExyRjNxWDd4aVdseDd5TUNqU1Zva1ZEbTMvZ1l6K1FZbUZCSFN4cmxzQVBYcy9RUEd4DQp4Qk9Pd2dBRTE2VFR5TlkzQUNKbkVka2g1TDQrVER0VzRGTldQRTUxM0FLT21lcC9xM2lYVE4zVmVKRDINCkVucUZyampNU2dOcTk4SHM0SWNkek0zcUJMaGlXbnpqUmpjRnNFSDFpQjFzdUJhbENoVEtvQ1VVcFIySQ0Kb0M4YnlRRmlhYi9zcFExcExDOWlYaGJ4bGU0MG85bDNoUlZIUlNRL1hLNm9rOEJndjkyOTZINldBc0xRDQpXUDdobWhsRDAzZDRMcjY2bi8wY0g4c3k0VnZMZVB3enVROGlMRE5mcXdPMlBzVzU2aTQyQkhwTGZtWUkNCmtXc2kxanJEVmtCcVg4anBqajAxNi9QRGRrMHU1NGZxNm5KVi9NcHNSdE5WNWhVUjBucm9URllGcUlPeA0KMFR4MGIvZDFKa2FmM20rSXRhcmp0UU8rMVkrMnc5eW9xMXpCRzJTL3ZiZXJobzBnbTlNVFdmRnRNNHRRDQpEdlNtRGdHVWhKUXcrM2x2K1hoclB1YU9tUlQrQTVWeDBSNVdQL2RzVmFYMzRvRFFKd0hwZlZQTkI0MXcNCkpqMVBKOXRXN1I3N0E4K2ZjclJwT01JdXREZzZXTXhWL1hEdHhsQzlqZzRQK0tPTmc2ZGRQOXlRdHRXbw0Ka1dxU1I1anNzR2RxOW5tS25RVEZ1STYvM0x5US9NSUdsK0tBVHF5ck1iUUVDMm1TczNNNksxODZLVmtjDQplUTAwMk8zOEtkYVlOU0ZpL0crMXM1bURveE5RSCtmUDIzcDZ3MWVUM08vYTM4WmdIV1VaTi9DaC8yOHINCjlhN015cmNueFg2OXZHZHFvSlVEbmZLaG5aOHYwd1JxVFhFTno4UWxSR2FHZ29CMWtNUFpjTG5NcmVNZw0KU09GQzlkdnNUZ1pnOXgzZEo3OVFHU3JQUXBEK1p6c25VZWIyMzQxalBXYXJUMTFxZU4rdE8wN25YeUNrDQpFL3diN2xzT1FvbmRMTFRNZU5ESlAxSmxwWTUydnBFaXl5V2REbnBPWWloajVwTEIzd09jRnBiby8weUcNCmhSM21tMlo5OHpsSjFnSnFLOEk0cE50c0d1TlJoNkJ2ekIzb2dRemVQdVUwdUVUSGdKeWhwdWxqbnREYQ0KRUYyaFJsSzBURW55aElROHFlNk5YV3pqMU1mRVBDRnR6MXFtcWdPVi80cG44L3VYaEI0UjF6dmFidnN2DQphTUxyVzgvcXRJTWFETkVabCtxWDBYNVltb3JHMUpyQWdBcnFIYWZ5cTdNZkp4elJ0MGJGMW5Mc0lmbjkNCmlMeHNISXNwQkxWZG5jUVJWcUU3U28zckRManNLQ1Z4SW82a1M2VDdRTXhNdSswdmovcnVuWjJsKzZtMw0KVHVpeG93SkJtNEhVSVBTcmZjNHM0QS9nWWV5MXMzaEpNLzN5bE9uaVpFSm9wSzhqb2NiVFgybFNFZkRGDQprU0NPRkFjdXg3dEdkWWJiMkhKS21JZmMvV1BudjA2YjhtNU81ZE1ReGRzRFJGUU9oTUVpczBiRTVGQkYNCkl3UkFZSWFoOFdBUFU3TGJQSTZ2RzV3aVJ2QzlnRmx5RXVyUjNZVk5WSEhGQkY5YWNTYnN2RUFNclB6Tw0KenFXME91YkhTaFpFdTlSS01IcXNtUjFXQmZVWGlkeUFsMXpRU1NEaFh1blFhNjZBQld3VDdnYk1CSSs2DQpqTFN6TkJZU3c3WXIveXNkZDBJdWJSWG9obzFaaFU2NGQyakxpd2JTajY1U2VvUmVhVmthWHdOajI2TGoNCjR6b3JOemJlUThYSGQ5L0ZFNitYblFRV05kVWtFN2xpT2M2SVJUbDJIVVczdGNxTTZLdW1HYlErUjlzdA0KZnhOM1EycDVrdnZUaEdRZjV1N0tMWW1ZVTU4Y0w2aU5KWjF1d0dmeVhzTFZXMjJwcHRYbndXQUIvVjY0DQpiZlpOUTRieUVVNFpBVnM5OVA0UGJIck9Vb3hZVkNPMytLVi82YjF0cnh0bXpkTUpYazdISSs3MGI3ZDUNCmhKY3FVYmlOT2l0d2V0akZBREhRaWVCOU1oZStmMG9ycUpFL0ErZFNZVzFFTGpMWEptUlBuc2s0VG5GSw0KQ3RGaUxuVEVmWDhZNUxuS0M3OWhQdm5pUVp0ZUJpUWx2Um9tMkpVeG1SczloWFpGbGF2bXowN25CTmRuDQpLS09za2dWZkxIWjJyQkxER3JhVWVqY1VJM0tOZjlMU1BoaWZQQ1BFUmEzT2gwQVBKZjEvZ2RrSFNxUlINCmJRYXhMbVhuaUdOZEI5MVlIVG9vUjdnT0lhS2w0amhOakNiWFdoYWs1VjVQUlJWTTRteW5WNmwyWTdncA0KZ2NvM3lGNUtDZkNTWGtRVnV1TWo4N3o2dFFMc3Boc25WSW9SWVJDUFp1bk05NldIVC9QQ3oyZ0RhVWpxDQprQmNwOVhIL2ZaVldzMElnWHB0djRMVVphNGkwbmxoMkZyREdNeFZ3eW8vUWRXWEJVaUs3NXhjNnljQmINCno1VkJYR2d5aDB6OGdRY2dsL3ZrazUyY1FuYWY2YUV1UkRreVZFOUR3MmNheFNXYVREMDJ4UTBiczE5cQ0Kdk1tWEp0Q1RPNUdXVE91Um8rTmhPaUpUSXd0ZDJUcC9LaWovbyt2TitKbyswZk15eUR5cFk4UnhkVFl4DQptQU1WRGY2ckE1endHaFZrYlVzVkV3MGpGaTZDY3hCNG0ycWx4eG95ZkZQS3NLZlN0OHJWcWp3M0kyTWoNCnVGS01EdGtRQkRFRzBjZGU0YStBVkZOZzFPbXkyVXp0TDNDbVM1OU04a3FEV3JTSDhkNGFpUW9NN0lkYw0KWURGSDZ3Ny9odWZqRHVHNlFwbldVTzYrbURYTk1XYkVVWStJSngrRkVjWkp5V0RYekg3eVlNR2RXTEM3DQpZa2h2UUhzektKMVhlL2JSRm4xNTgxZE1TQUF6aWg2S2lQMEdBK3pJakhQQ09xc0lrdU5UTU1OaTFwa0INCnh1UFVwNEVXcHpEeWVXaFlUR3h0bzhkNDZTSU5XMGFuN25XRlhNZjhCZHFNS0dUeURad0l6dEdMSTllTw0KMzRYZjlTYURRa2tXeXdsUm1jbm83WGFBbXlsanNsaXRFcldnbDkwam4vZzQrZnR1MnVvZDMybXZ5WFNVDQpoMWkxNWtKamw3QS8wVzdydnlEaDh0R0hkYmJid2dRVzZ2MGhqY2NkTUxTbkNoQ3lJUFgrbWQ2T3llck8NCkRMRjVWQlMrRHpaN1EwK01pU1hJbkdKQVJzTW1STEVBcE9NZDdudlp1SmZYNS8zYnRmT0FrdmRkMUNBcQ0KWmJ2UTZKdXJTYXlSZ0lRclR2dVk2c1RlYjcwaEV1ZWdEWDVLamRZRGFaUzlkYVF0NkJuQk9pU0hGRS85DQppTmRRS2YxVnNGRFQrdmtVWTlPWUtOQWVKeC9wRHBGZFRHV2ZOcG1FNUkyZDJHUzBnSktpOVB6OEV0YmUNCjRpZFk5dURTM3ZHanJIckdzNEZOellqWUtpODh3NjNvODZieStEWDEzcTYyRDZ6Q3FWTjFuUjV1dlR0bw0KLzZXaTJwekdqam5OSzVtQlQyRS9VVktEUFFRU20yRm5Ta3Y0YTM5SUxGaDRISXBxazlveU5Gd0tlM3prDQowbG5DUk1pMlBKOUNkTlhIdGxMeHIzellBd2VFTUROVlJ3ODV4Y3VUSmduYktveHFlRnQ5SjRTR0ZsMWsNCjlzOTF5ZWxOSThuS2x2bHcwSm8xZUt2YW05MCtkeVFjRXoxbWZ3RXhGRWhEZC9EZ0JYTFk2VVoxcjdqcA0KUm9OdWE4TnBwQXYxWEtRdGt6anFLSWVhWlZJMkk1elMveU12ODNPZ1RwZFBLSXY5cW1OVy9MWFMwZnZXDQpPYThmWHgyQ1ZBc0NHaFhFNEg5dXNwcEo0QzN0bVVDUmpzRnZ2ekRCQXdUdEtEN0UzdjJyK1k3R3U4alMNCm5yOWphbERrbXFud0gwSUNGcWw4SmRIbHBCb3Fhd0VzdkJOWkJkQ0pNWTZKVjQydlFnZ1JYeHh3Q3lDcQ0KYjhaeEMraVhFazFUQXVKUVMwQ29PMFRSN01TSlJzTlhQaW9jeWV6ckk2Wmh2YnJnOFhWeFBnZnRsVWM4DQpVMytRVGtIMXFhQnV3aldOUnM1cVhWSm8xK3J1a0VEdWhNRU1Fbmd0aUNrNmV6YXdjV0d2TDJsZWwzQlANCkI1N1JXS2w5MG1QaHNZV3JWdVFOVFo5RGNPVGt1bzJJeWVuWmpUYWY4ZWdYRGJndWwzV1hlVkNyZkVSMw0KUWpBOWQ3cTY0VWVCUDVxdkcxVmU2SXNOOEUyOWhNVGR0dXhycnZONzljZHJzalBBRUNEMXlhZk5lTUt2DQpIbFdsZzc0azUxNEtkVENxUkpWQVE5ejR1b010QkkzRDJFLzQ5YzNmTGdEQzhkbCtTVWl4SnhpRm5LYVgNClg2SWtRek5wR2wvUENOV2J2YWxacTFnUFZ4M1RDc1ZlWlFleDFYS0x5TjFGa0g0NEdlWFRaZWhGWjYxTg0KQSttWEYvd0lDMlZXSUZzN1NkaXdUQ1FBbUc5M0h4QVRZbm9rZGRBRWgxMXFFQVIzOEloTFRNbko1b3pLDQphU0xKUHB3aHVOcVpTeXVvbnhlVThCZDVlcUpSd2RzeUpLdG5lVk93TGJvMnNFNzQ0RVJSUHZlY1NmWXQNCmxEeWJUdHdCYXc2Y0NxNFhuMWRuZU1FeWs4YVd3N1VmaGhOUWs2MUVkdVlKQXZ3YllkS0hWOTR2QjZyUg0KU1lNd3pJaG12UzV2dzRnalRwOTcvS2RRcFRpRE85TlYvR1lNT1h6VlA2blVUU1NqMktHVEU0WVB1emlnDQpqWFkwTTZXcjFxWjQ2S0E0ZFdzMXd3dFVIeUpjSkVaTWwyeGdzb3JRSWNvZGRob0FDU09VWmd6bnZ2akoNCnU2K0pQTlNCQjBFWDZsVitrMndaNGNha2tzM2tqZlZvdW9OdDdQRzBlY1ZzVlM4cGZsTEdVSkNBeDZZbg0KYVRVS2FYRkJUeENOcUliZGRwZ1F5eUtSdmxwOXlCeVFPV2p4T04xV3g3d0t1NlpDYTUwQWVsYlBnb1dXDQpxL1JJMTF1NS9XMU1STlc0V3pyVjlyeC9IeWpvb091YkpndHN4TjVyVVBOTmZRemxJdW8vQUdFMXBHb0INCkNOSktmMTFXdjF5VXE3bGM2QVB0T2d0R3BsQlhDYVhKWVBrd1krV0gyaXM1Z00rRzB5UXlEYVNvQ2pWSA0KNklhMDJma0FHdW9kQ2RKK0VsRDIyRVNYYlZydlQ0cHRmSEYraTcyU1pHM3o2WDY5UmRIVjNzY295WkpZDQpoekNyeGFoamhzVU9UNUo1MDJaQ3h4S3pjUllhbEtQS0FTRnI5TVJCL0d1NkZPV2p0cVpwSCt1dlprWFoNCk1wa2RHRTBaUHNsSFlWeW9WL3hYeElmMy9yU25hVU0xVFZUcGVmSjlHOUZaSmF3MlNqR01zUzRMYTlLTw0KVEt5V3ZYdXNORGtBLzdDNVhtNmUvUVBPZlVNZy9CUngxNThmeTNDUkxkcksrbDdHWEdxQW9aSFZyOHB3DQo0UVdUc3pSVmtwakR6TnFjbU1LOTZRQkZNT0t1cUNqMGR1YTJjNXBZZVJ1MllTaEw4UHpQN21kRlZWWWENCjZPYUhTSkR4eVpOOWpGNU1Fc2JHUDRnWkp5U05senJlQmlyZTFrMW9mZXp5UG4xcGpIeTdXSXRNWlptSA0KbzRqRzROYmVYc2FDRXJjMnVQOFN2dEllMTV4WXJ2OGc3blNvM2V0VFlnUjA4NXk3cFVZV3Z3VEpTOEF5DQpaUUhCTmdNUm8wZS9pL3ZXOEF6cGRLNmg4OUtVL3A0M3ovR0lhY2FJZCsvT2VTeXJSRkxYWnAxMEhEVVQNCmFSL3NoelBOZzZMRUttZXhkSm45TVVJUjhlcC9rZEo4RmFhd1RKQmNJMzdpbnVuYWZya0FrVWxSeXhKTQ0KRTh2Tzh1ZDhjQWxlbkJ1eFlxeVNlZkJZTEtHN3QwL3ZrempkVUJUQlJ6VGlaZ2hlWVBBL2o4bS9pUW9hDQo1ZXM0Q0dyc0lPOWcrVEdMRGxqQUc0ZlVseENybTZOMFlxaW96NXhEMldBVTJJT0dYOGFOcmIvL2E5aFQNCmV2V1ZCa09wS2FHdGlTMEUyL1lscEkwdlhXa3pCK2tYeWpYdmoxZzZUSUZWaW41RXJZZ1lQdXFrZWgreQ0KeWxxeFRiZ3FTc01DbzBBbHg3ZmY5ZTEzY0pMR0dGdjE5N20xVzdwbFVEYlVEWisrS3lYT0N2OEg4eUswDQpNSkhzNjdSQUZOVHhsN0dJY0x2UjhCMlBQeW8xR3ZvVWV2WXJjc1c2dGRHL3VpdVBDakxxQjV6VkJ3SngNCkMzUVdzeVA5OERyL0NEQjladHp0QXEzeG8zSlpSNWVKbDBKeGRYSVlIUFFMQnpkV1k0RmJobWJsZDdDdw0KSHFhVnRMWU5BZVhhWjAyTGhzSHBxVXRQYWFEM0M4T05oQnRUQVlhaWlzdkFXUW9lL3RmMG5RYXg5cDVvDQpDWjN0aEhOZU9hSVdUQnZmRFl2bkRYazJvRXUvOHVQUW5vZW1ZTTVoT1BPOUp3R2NDMWgyc01YVmw3WXcNCkFHbWhwTE91aWRjWVg4YmViMGNDYWVOZFk2N1RrTjF1WWo4U1NXNVoyQjVCVVp2Q2hrT2FHVHFzMHNnLw0KR1dkZzlHMmRXMlhTZWlCYk1oTE0yUVdjc3pHY3M5QzFaNFhCZGprZEZ6bUJXL0pDRnJmZU1ybmpTVWZoDQoxcFY4UHJCVEVHRlJ3dEs0RWpHTkdDdTFCVFlVVzdpb3g2T3g2ZU8zVjN1V2ZwL2ZXVCtVK1I5d2lucjENCkpySG5rSng4dVBJU0ZvbWEzall1MDl6VW93cmxhaHdmcS9SOU5hTkZlVFpxbkd3KzF3ZGhRQ3pZNlNybw0KVy9tNlkweUZ2ZjZvWkpPMzZ0TkNReXhMeUZBQUtzZmt4c0F2S0l2NG5OeUlqbnJKTlJRd2JrZWowNXV6DQptN01TaWNDN1RNMmova2RSS3cycXNMemczZlhieEN4WmVSdTFFT2FnazZKOGVuaGVOTFh4SDZOekh4aTQNCldtMzh1ZUw4ZGErdHd5eDN6TE9iZGVXQVRMSXVRTzVzSzUvdTU5KzI1WWNDSTdtL0JFMlJBY2pwcUp4Tg0KZllNd0ZtZUJYK1YzRDREbE5QMytJWTNqRGxKWlVzQ0M0TUJ1Z1JPRUd1Q2l0bFg1empBeGtYck1oRkR5DQpwak4xSGJ0ZGxtOUwvVm1iYldsSmdFK2hxNldUUlBtRHF1eGNCa2xNMlRUbGJCdFUrb3hIbDF3QkVGZHkNClV0eXJlSVplMklmUHgvV0VQbStTQjkzMkp5cnI0ejh4TnRubVlud0ptWTFzRU5YRTBKa2taVkp0alJ2VQ0KU2hvaVd6ZEtOTEU2OFBjN2QvOWNBTGhQMzF0L1VxSkh3WDhucmI3M0dLY3BzcHMyb0M1WVJEaGtaV3dIDQp0RjBIRzJ1YXExRkhVKzZQT1kyOVg4dFdCYmlrVkhzYnI4aHBYajNPVS9HWUpmcjdtRlM5NXJUazFNVTQNCi9ld2wwQi84ekN6WDdpTWVpUVUvOXRQMjFUa3V2QTY0SlI4eTBxRFRheUlIV3VOcTlJc0hIMkxuTE5sQg0KWjZmdjFaTkNsTE5mQ2FVbXNCeGo5NE5zNlc4cXJROGk1V1Iwa2Y4Y0NKcnM2a3pPb3VEVEhnRU9rZUltDQpSR05YYytVMzEvcExIdmhKR1FMYVFZZ09JeE1qZm1XcWNONWpWZVAxUWxoVndab2ZyWjVqTng4QlgxZEkNCjU4ZktuQVhZbkRSYXNCOUViT1VOMWt5ZEtjVXVlR2M5ZG9ER3BiTGpmQVlFMXFVdkdhbzBRTWVadmVZQQ0KcGtldVhON0k1TkF6cGwzTmI3TnFLb1lPU1Q0WElMVkNMMGVsYWdIRU5FamtacG4xUVl6WEpoZzRiaTNCDQpKTERGSllpRlEwZ3JmS3gwZWZtbVdPTnFWK0pMSlk3Sk9xTnVtbWlPVEZmdFhRWFUwYkpCQStmbkUzb00NCjJTbUFWcjJzSCsybjNMRDZFa081MVBzbDZjMzBEbnc5Mm5YK015VlVXd1hLWUh5SEZoUVIvQ2x4TVJBVA0KYkFZckI4T0JwVzR0QzR0UmZSdWhlQUhNQXpSa3R3MkN1c3dSeXdLNmg3RFBZdEZtZEVDTlh0d1duVGduDQpxQ1VOQ1lsYXlDQWNTWXA1WHVRSk5Xd3cza3piZm5LWUFzVDUrYjRVRmIzKzI2dUZ2MkE4S3gwWnRXRHINCkdzbkdFeXU1c3pMRTFsR3Y4TTRlcWJHTDhkNlJqd0s0Sk5mZXNWSkRIVDF4V05LbGU2aWsyM1JNZmpMWQ0KNVpPdkpTT2c3UFFNQjE2RGp1MlVGNDdKbk91WWo3a25mSFdCNnpUR2s5Q0pLN0NmMlRPTTJ6WGZOdTMyDQpEQmZ2V2tHTkZTQ3A2bEozLytFQk9PVTBieXkybmRwNFVodG1YV1FVUW01ZHZ1QTNBemdMMVQwNFc3Z1kNCmFKWTVDVHk1ekE4MlkrNHVsY2pXVHl1djhzMzBZRy9lWUVMY09idWNzMU1GMWdBWWFXTTNBR3hhVmVIRQ0KS2ZiU3U0ais1ZnZQMTZ3WFJpVWF3aHhHQ2tOSSs2Z0hKaWR1UlB3TC9NN1NkaTR6MUhSd2FGMDBudWlxDQpialBCeUQ5QUNMWDNQb3FCd0U0d0VIbDVCcWlPbU4wSmRzaXhITkFqQ1B1dUd2NWpBem5LUzNqQzZ4SysNCjlLRmU0TStEOVhqbWdONktLUEMyZ01kTU1IY056NER6U0poNDlSOVlCTW50WnQ3QzhtaU9jeU83UWYrVg0Kd0tqUFI5N2hUdU5DSXovQW9VRDhrL0NyZnBLUUVMT3dxTHN3cmk3Rm0xbWUwMFliK3FqQXVTWW5ja3B5DQoxMDRWOFVUdjZ1WXZ6KzEvUG1uSFZoQlFGMi90SW8vWjVkZzJWeG9nMTBjWGFDcWFsMXVwNUFWdG5ZWEENCmpCdkFLSXArMzRSKyszamVwNmFtQ1gvVXR6TXhWYVlITnF6WXZoVkczS2o5cXZabk9BWjhJR2E4QkZrag0KdzkrYVBFdkxucG5oNjhwOWc0MWpZYjJDbE1lK3BkREtRZWZ1RUdDNFduUXJIYkRqc3NPcmJEek8vaVJnDQpyMWc0WkFzREwyc1UxS3p1NW9uak9sQzRKR29mUDdicWpVUmdTZzZrSXY1UVg0Y0FMclo2VnpJNzhLME0NCmFZNCt4N0h3ajBmdjBIeWFVVURZbmxXZVlkZERzRVloSEtSVHJ5K2lNM0tzWStsSnczTlZEQkJJbkdZdQ0KUHdBWUJyVE1jWWVkQmwweStLTDIvUDRVYS90Q1l5Zm9nN1hzTU53STJFN29Mcnd1YmI1VVpkWTVJVUgzDQpxeDZ0b3VVa2ROaldUT3N3cFhBSXhwSHRBUWJGN2NEYzRpVjZMOFZETnNwcEtuMnpwOVVHcDB4Q0NZZ2MNCnhCVzE1RTNBUDdSdnNxQTdWVWc0cXYzcnliT3h2Rk12enRVaFpsZ2Q3Um00N0YyR2xiWXdQY2h2SHlGRw0KcU1DN09mUmFjTjZzRVRpdWs3OHJCWHhVQlY0STN0cXN3RGxOZE5GY25iZDZMUmZHNnZoajhlZE9BRCtlDQpBdnpWcGRROUlUVHoxd2YrNTQ0TkVzemNwdDdTN2xuNFkvYnNRUitSZXBLajZGeEdCNDNGWFk4WkNaMWINClpUSnN4Rk5STkd0U2d2RysyRHBIWjhwQ2JETmFHby9mZlRMQnY4bVlHRXBFUXBDbXpsYm5KaVlxa2hRRg0KZlVNeGFiL1pqOHlwWEU4WlRCSXl3NHNJZStRckcxMTRiR2x3ejJJMnhXOGdNOW9ES2luUEpKcXR5M1h3DQpCdEI1VXRGU0s2MHdGMUlsZ2pTMFFWTzR0c21rDQo9OFRUNA0KLS0tLS1FTkQgUEdQIE1FU1NBR0UtLS0tLQ0K", + "size": 17118 + } + }, + "raw": { + "id": "1850f9608240f758", + "threadId": "1850f9608240f758", + "labelIds": [ + "Label_15", + "SENT", + "INBOX" + ], + "snippet": "", + "sizeEstimate": 24840, + "raw": "UmVjZWl2ZWQ6IGZyb20gNzE3Mjg0NzMwMjQ0DQoJbmFtZWQgdW5rbm93bg0KCWJ5IGdtYWlsYXBpLmdvb2dsZS5jb20NCgl3aXRoIEhUVFBSRVNUOw0KCVR1ZSwgMTMgRGVjIDIwMjIgMjM6NDI6NDggLTA4MDANCkNvbnRlbnQtVHlwZTogbXVsdGlwYXJ0L2VuY3J5cHRlZDsgcHJvdG9jb2w9ImFwcGxpY2F0aW9uL3BncC1lbmNyeXB0ZWQiOw0KIGJvdW5kYXJ5PSItLS0tc2luaWthZWwtPz1fMS0xNjcxMDAzNzY3MDU3MC43NzEzNDk2MjI1MjgyMjU2Ig0KT3BlbnBncDogaWQ9RThGMDUxN0JBNkQ3REFCNjA4MUM5NkU0QURBQzI3OUM5NTA5MzIwNw0KRnJvbTogRmxvd0NyeXB0IENvbXBhdGliaWxpdHkgPGZsb3djcnlwdC5jb21wYXRpYmlsaXR5QGdtYWlsLmNvbT4NClRvOiBGbG93Q3J5cHQgQ29tcGF0aWJpbGl0eSA8Zmxvd2NyeXB0LmNvbXBhdGliaWxpdHlAZ21haWwuY29tPg0KU3ViamVjdDogVGVzdCBtZXNzYWdlIHdpdGggYmFzZTY0IGltYWdlDQpEYXRlOiBUdWUsIDEzIERlYyAyMDIyIDIzOjQyOjQ4IC0wODAwDQpNZXNzYWdlLUlkOiA8Q0FLYnVMVG9xU0dWTjFQWjRnOEZBaytUbkpQNXB2eVBlZTV2U2FXPTItNGpYaTZaeTRnQG1haWwuZ21haWwuY29tPg0KTUlNRS1WZXJzaW9uOiAxLjANCg0KLS0tLS0tc2luaWthZWwtPz1fMS0xNjcxMDAzNzY3MDU3MC43NzEzNDk2MjI1MjgyMjU2DQpDb250ZW50LVR5cGU6IGFwcGxpY2F0aW9uL3BncC1lbmNyeXB0ZWQ7IG5hbWU9DQpDb250ZW50LURlc2NyaXB0aW9uOiBQR1AvTUlNRSB2ZXJzaW9uIGlkZW50aWZpY2F0aW9uDQpDb250ZW50LURpc3Bvc2l0aW9uOiBhdHRhY2htZW50DQpYLUF0dGFjaG1lbnQtSWQ6IGZfblNvREJVaUhjeGVrUVBMVElQVkp6dWpZSFBuQnpvQGZsb3djcnlwdA0KQ29udGVudC1JZDogPGZfblNvREJVaUhjeGVrUVBMVElQVkp6dWpZSFBuQnpvQGZsb3djcnlwdD4NCkNvbnRlbnQtVHJhbnNmZXItRW5jb2Rpbmc6IGJhc2U2NA0KDQpWbVZ5YzJsdmJqb2dNUT09DQotLS0tLS1zaW5pa2FlbC0_PV8xLTE2NzEwMDM3NjcwNTcwLjc3MTM0OTYyMjUyODIyNTYNCkNvbnRlbnQtVHlwZTogYXBwbGljYXRpb24vb2N0ZXQtc3RyZWFtOyBuYW1lPWVuY3J5cHRlZC5hc2MNCkNvbnRlbnQtRGVzY3JpcHRpb246IE9wZW5QR1AgZW5jcnlwdGVkIG1lc3NhZ2UNCkNvbnRlbnQtRGlzcG9zaXRpb246IGlubGluZTsgZmlsZW5hbWU9ZW5jcnlwdGVkLmFzYw0KWC1BdHRhY2htZW50LUlkOiBmX2FSV29QdEJvRHBPVXF6UWhUVWpOWW9QaUtSWk9nVkBmbG93Y3J5cHQNCkNvbnRlbnQtSWQ6IDxmX2FSV29QdEJvRHBPVXF6UWhUVWpOWW9QaUtSWk9nVkBmbG93Y3J5cHQ-DQpDb250ZW50LVRyYW5zZmVyLUVuY29kaW5nOiBiYXNlNjQNCg0KTFMwdExTMUNSVWRKVGlCUVIxQWdUVVZUVTBGSFJTMHRMUzB0RFFwV1pYSnphVzl1T2lCR2JHOTNRM0o1Y0hRZ1JXMWhhV3dnUlc1ag0KY25sd2RHbHZiaUE0TGpNdU9BMEtRMjl0YldWdWREb2dVMlZoYld4bGMzTnNlU0J6Wlc1a0lHRnVaQ0J5WldObGFYWmxJR1Z1WTNKNQ0KY0hSbFpDQmxiV0ZwYkEwS0RRcDNWalJFWVdaVFZFeGxVWFlyYkZWVFFWRmtRVmxEZVU5dVNGZGFNRkpsTW1oUVRqaHNSbnBDVEVwTg0KUzFsdGRXWkxRVTB5WkdoSldWcEtNazROQ2xnd1JYZDRVbE5TY0ZKS2JsbzJVMUZEYjBKWE1uRk1aRXB6U21sblpYZHZMMkpWU0VSRA0KZGtWYWNYaFdaelJUZFVWRVZUaFlSR3RuWm1aM1pBMEtNRXRsWjA5SVRVTjNZMFpOUVRCMFlVd3ZlbTFNV2xWQ1FWRXZLMHBHUlZoRw0KVFd0a1RVSk5ObWxVUkVKMU1sY3plRzVHS3pkaEt5OXpURWRQRFFwTFVUazRVRlZUWjA1elVFTlRXbk5tVWpGSGVraFRkWFZwWWxRMg0KU0hWRE1HOUtSRlJHV2s5bE1VWjJjbFY1WWxwWU5YcGFNbmQyT1UxUVQzVU5DakZHUmpaSWNTdEdjRVpuY1RaemVIYzVXVEowZVhGcg0KZEVWQ1RUUmhRVXhSV0RWTWNHTnFaR3BqY0VWRVRtcGhNa0l2WkZaaUswNXRUWGN3UmcwS1NsWmpjMjlhYnk5RlVVOW1VMDlRV21aRg0KWm1acmJrdFdiR0p6WVdWVWRXRnNUbTVMUWxWdk1VNXFWRXRrZGtwRWVuTk1TakZLYzFrd2NuaGxEUXBYV0ZWaGQwWTRaRnBsYzJ4Tg0KUjBnMVIxaFliMGcyZFdsQ2FsZEtlVkJQVUdsSU1tWnVhbGh0V0ZsNE1XZG5lbG9yU1ZSRVJsbFdabEZXTUdzTkNqSnlSRUZ0YjJ4bw0KVDB0aFZsSlhRWFowTTJnMWEzQjZWWEl2YVU1bU9XTm1UR3BHWTFab1YxRkZaRUpZTTNwc1RtWTVkMEUyYm1admVtWTRSZzBLYTIxRQ0KWmxkd1pWQmthWGRtYlUxS1lWQlRZa1ZuTTJSUmNHUXZlVGQ1TUdvdlRsTkdVR2syTVhNMGNWSTJUV1IxU1dwelF5dEhNM0JJU0d3MA0KRFFwa2VVTlFPRzFLUkhoQ01qVXhia1Z6VkN0MFZVdElWVFJIYVdaV1RtRkVabFJPYTFnNVZGaHJTWEF6ZFUwME5IaEtlRmxzTWtNdw0KUjJsQk9FUU5DbkJaV2k5TVlsaE5kSGRSTkV0MU1WRlFibHBNYldsVFJtSTVUbEpTVkZaNWFrMW1lR1JRY0ZaSGNucFhNamh3VlRkcw0KTURCRFl6VjZjRWhQTUEwS1JXTlVNMUZZT0VGNU5XSnBlazl1YTBsSGJ6UlRRWEJsTUdWb1l6bHFaSE5rTWxRNFExQlJlQzgyYWpWNA0KWnpkRFZFTkVZalpOYW1oaWJEUklEUXA1YWxrd1lVaG1ZU3N3U3pKTVFqbFdValpuVkVGR05YbDNkRGd3U1VkUmJUUnROV3h0YzJadw0KYUdWeWNTOXhVM0k0VXl0RFYzQXZPVTVtZEZJTkNrNUVNMUZIVkRsUlpEWk5NelpsTURGMVVXSkJhVlU0ZFdSRWRrbExLM0F4T1daeQ0KV1VSaFdWbE1lRWxaWTBaU2FtWlVXRUozVlhkRWRtSXhNZzBLV1ZCaFdtcGpVVUpFTDNOSWVtbzJZbEpRT0U5blVISkNibWR1ZUd4dQ0KTkRaT016VkVhR2xMU0c1Rk1GRklhRkEwYVdRd05HbFdhRUZIUzJoeERRcHVPVkFyZFcxNGRsQm1VbWxNWWpkRE1IUXJlRGx6Y0hadQ0KZWswdmJHOWpOMnBGUldGeFExTm1WV2xSYWs5S2NsQkZVRnBMTmpaNFZFRk9WbElOQ2xKSVZIVnVXbnBqVVdJcmJUWTJNek4zVjNoTQ0KZEZSNFZEWXZSVVZSY0hOMk5ERkJOM0JFYUVvMlRrOURRVWhUVWpOclNXNVJVblpvTWk5NllRMEtSREYzVUZwd1VtUlZha2g0YkUxNg0KUVdSTVRuaGlSVFU1V21ac2RtWlhLMlpLVlcwMFYwSlhNakUxUkdaVVJIQjRaV1EyV1hkelpGQnFiRTkxRFFwYVQzRktOVlF5VkhZMQ0KVlRSc09XMDNia0psWXl0TlFYVlFaMjV4VW5WQmJuRmFRMVp1WjBOcGQwa3hZbFJCWTNRd2FTdGhibEp6WkVaTVJVc05DbFUxYTBwdg0KZG5KVlMwUjNTVGx5Vm0xclNIZHBaMkVyVEZScE1XOVVTRkJHVGpsRFRuQkdNbHB1TDIxWk5tWmxaMVo0Wm1kNVdUQmthekpPT1EwSw0KYVdaM1NVUnlhVVZaY1V3NFlsSjZNSGR3TnpaTUsyZHJOM281YjFrM1pqZExZWEJ4ZG1SNFowNTBVbGQxWkhweWNFWkxaMFZaWjFRMw0KZDNVekRRb3lSbmsxWlhCeVVuVm1TR1p4UlUxd1MwTndXRmhCWVVocWIzSTBaVFYwTWk5blZURnNOVE5hYmtoTlNWWnBNV2RuVFZSNA0KTmxSeVdGUmxhbGNOQ2taME1YSk9kVWhxYUV0a09FTnFUa3REUzFWMVQyRXdNQ3R4Um5nMFRqa3pSSFp3SzFCRWF6azJhVmsyTDFSWA0KTkRCMVkwa3ZjMGRSYURsNFdnMEtSR05NZWtoS1drWllkMWxXU0VneWRIRlFlamh5YUhkbWJFUkxZVWh4ZHpCQlNIaHpWemx5V21Jcg0KYkRZNWVHRTVWVWQ0UTBKd2NUTTVSRWRGRFFwVUwweFJUa1J0VlV0TFlYVm9OVFpGTVZSb01WazBjbTVhUWpWMGJEUkhha1ZNUjFwSA0KUkdSVE9GSndTM053WTJGbVF6aDFWMFZDVVZRMFVFb05DbW8wVVRscVdETlRTVmhrUTFNNVUyOVFRazVKYzFacFdrTXhhbk5IZGtGTw0KUlhOSVFsUkJUa3hYYVM4NE5Xa3lWa0ZSUlZBdlVtTjNSbWwwWncwS05tTnhZbVJRUzJoQlVtNXFOMUZzVmtwNWJHbDJOMkZzVldSMQ0KVUU1S04yVmhMMlprV2pGMlZ6RmthMmN3TlRSV2JHbDBZa0pQV1hkTkwyVlNEUXBaUmtneWVFMW5WVlowTjJOTVRqTm9iWGxGU21KMw0KTW5ReGQxTnJTVXAwVm14TlJHUlFXVVZIVEhaeWJUbHdRVWhpYTNaSlREZzFlV1ZuV1ZnTkNubDNSVFZIVkVwSk5FMHpkamxLUWxCYQ0KYlZCRFUyZFVTMEZLVVZaVFUwOWxkbmRqUTI5UlJHdGhhMEpFYkVocGFUVm9abWhqUlhwbk1GWkdVdzBLVUdneFJuUjVTREI1T1VOeQ0KV1VaUU9XRnlVUzlCTTJ3eVVUQlljSEpuYlRoaU5VRkpSME5zVUVwdlpEUTVka050UW5GS2IySTFiMlZaUVROeURRcHFVMUJXYTIxbA0KYkRVd1ZVbENZVlpPT1VwMEwxcG5ZamxqZGxCNVltVTRNVkZqYjBjM0x5OHdWeko2WkVjeVRIWk5XbFpETlhsMlltSmlhakVOQ2poaw0KSzA5ak5qRk1TVEp6WTBoclZuRlFTakJNY0d0dVluTk9iR1ZJVVdSMVprTXZUSGhUTTFSUVZIcGtkVFJ3VEVkR1owZHZSSEkxTm1veA0KVXcwS1FYZFBUMGhRYW5GRlUwNVJWVVY2YUhRNWFtZE5WREUwZERsRWRWTkhabTEyTkRkTU1sb3pkbEpKTkRSdVUyWnRNMjVKYzI5Ug0KVjNOUGRqRnNEUW8yYUROSVZsSkJNRFpaTHl0SU5ITm5lVzFSYlhkdk1HZzFVRWxqTldWS1RsSmphRnBPWkVOYVNISTNibHBPWWtwcg0KWWt4QldYTnpWaTlOWTBRTkNtaFVaRlJpVDFGNFVua3dRbTVrVG5oa1IwaHRWa2RuTkhGR1ZsSm9iM1pOYkc1WlEwNDVPWGRFYWl0eQ0KVVZBelEwSnJRekZ3VGs5a1QyeHhRdzBLUVc5cVZHMVNWVk5RU21GRFJHVnRkazlVUkZrME9FWTBjMUE0V0UxR1RHazVkR0V3TmxOYQ0KUkdaVlNtVnFjMHM1ZFRBdlZreGpiUzlQYWxwekRRcGxablUyY0ZONU9FYzRja0pCSzNacFprWlVjbmsyTkZkNlpuUk1VbEV6U1ZGVA0KWW10UkwyNXNlVWxDTm5wUVYxRk5iblZ3ZUdWMmExZHNNMHNOQ2tKQ0swSTNSWFpGWldGeFoxbDZZa2gzWTBaTlFUY3lPV1J0UkRKdA0KV1RORlFWRXZMMlpRTUhkclUybHhZbk5VU0d4RmFUaGFhV1VyYjNjM1F3MEtSblZCVDJsVmJIVmpia1psSzI1RlZpOVNMMmh6VDJ3dw0KU1RRMGRWUm1VR05aWTBSVlZIcG9PSHB5ZUdkRllXaDBaRXBaUmxCS09FNUlZVzFoRFFwWWMycE1UVU5qZGtkTGNUYzBlbWxoZEhKMA0KUTBwa2JYRjZTakpNVHpaSWFWUmFUM1pVU25oeVNVVXlRVmhvWldONVZVTjBRbm8zY1RCMU4yZ05DblZyTlhWaVZFdGhVVXB0VEdwMQ0KTVZGWWVETlRibk5tTVRrNFkxb3dVR2xRVERodU1qTlJSbFZDV1RnNVJFSkJSMkZDTkdNemFHNXZkemRGT1EwS2NITkdXSFo1YWsxQg0KVlRkVmNGTnBRV3hwU2s5c2FscG1WM2RuWWtOb2VIVlFXWGRNUjNoQ1ZVdEVXbEppTUUwd1lrMTRSV015V1c5TlQxWjFEUXBCVmk5dQ0KYlRJMWFISm9abWhuYlhCTk1VUTRiMmx4ZVZwWWRXcGxlVXBaZW1kNWFuQllUa28yU1hGM2VrdFVhVWMyUjFwSFUwb3hRVmcxUzNnTg0KQ214Qk5uSlRkRGRrUVdaQmJIazJNMlpvWVdjclVWaFRXSEpsYm1SaWQwbHBSVlZtWW13M01tZGhTbFJ0TmtWTmJrNTNWbmh6VDNCeg0KZFRRM053MEtaeTluYkc1VlJubFZORVkzTjA0MmVXcEpLMkpWWlRaamIwTXhXbmxxVFRWNVZUVXpOVFZsYUU5d1kzaHNUMGt5VFZKdw0KVURGeFpuazNTVlp3RFFwTllrOUpaR3RNVGs1alQxaDZRM0Z1T1ZkeFNGUnFVVVZhT0VOV01TOXFLM2wxUVRSblRqSTNOekZQVTNsMQ0KUW1rM1QxQnFVR2RGTVhKM2NTOE5Dazh4TmpoWlZIQlNZM2xNUkhCb1JFcHBUa2xJTVZsblZVZElkV3hNV0ZaNFlVcEJNekk1VWpaSg0KUjFJMlRYRlVURzFzUVZWcFIzSm9lVzlLY1EwS2RHc3JObGRWUWxCTVVFa3JZa3gwTlRaNlVubE9MMlJqWjBoTmIzUm1ORUpVTlZaWg0KUmxkbVJrZDNaMUpYZGpCRGMwaG1hbk4yTTI1RVlVWkJEUXBCZERSdVRrVklVblkzT1VOTU5IRjJURkI1WlM5QlExcGhUM2RqTWtSdg0KVUhoSWMybFNUakl6U25OdFVHeDZNRlYxWW01YVltNW5kVlI1V0ZNTkNpOTNRVUZLTTBGQ1pWcEpUMkl2WmpsNk0yTXJaek56TkhOSA0KUWtGRlJVcHdXRzEyZDB4d2QzTkJVMjV4VmxOVVJVRTFPVTlVVldOMFEwZE5OUTBLVHpaMU16ZDJkRVZXYVRab1NFWTJWMUpQUnpOTA0KVG5saU1IWTJaV1o1TlN0MlpFc3hVMDFrWVdkclJsZHBUbXhIUkd0bk0wZ3JPVkZ6TDNGMkRRcDBjbkZDTnpoTFVGVjJjVFJFWVZsNA0KWm1oa2N5OXRhWFpHUjJ4RVIxWjJOV1UyZGs4emJsVlNTbTVzYlUxeFJXeFpRV2xHVlRWaFdpdFZUMUlOQ2toeFJreHpWbVpxYlRGcQ0KVFc1SE9WaEJlSFZLUkRsV2QxaDBRVU5ETlcxdFpuQjBNV1pRT0hwS1dYaGtNQ3RSVG5nek55c3dOVkJ0UmpsRVdnMEtlRTFXYWtoSA0KYjFNNVNuRkhjVkJ5T1VkQk56VjZlVkpoTm10eGFVOTZSMVJKZDBsUFRtNU5hWEpSZDNaek0xTnZaREp0TW5aTFFVNVpVMWhXRFFveA0KVnpOT2FYazFVbWRMTVdwa1dGQXhNRXRRVXpWUFVFRlFkemRyTjJWbVF6bE9TMDVyUm5aWmFFdFRTVmxHVDJWU2VtcFpVamxxTWtKcQ0KYVhVTkNuVlhkekZUWlVKcWNDdGpTV2R2YmtSU2VUSXlibGRzTUVwTFNrZHpUMEVyV2l0M1NFY3lXbkpsWlVKSVdYUm9jV1YzZURkdg0KVjJkQlJFOHdWZzBLVnpoT016VnNOakJXVFVSdlZscGlSRU50TW5aNGRXOXFUMk53TUhOSVltcGhPVzAxUkZWWmFuRnZkelZwU1ZGVg0KTTNWUVoxQmhOemxNT0ZFNERRcEVWMmhSWms0NWJqQlZWbXd5YWtaTFRrTmpaWFZ3VTB0Tk5FOTJkWHBYV0RoSFNGTmphR0UzVVRCTw0KSzFCQmVqTldNMGcyTWxSREx6WkdhVlFOQ2tvNGRYUnliMHAzY1dzdmNFeERObHBKTUZWR1dVUXZkbVJvUlRBemFqVm5OMWhXVUc1Qg0KUlN0UlVYVkVlbTlYTVRoak9GRkJiRVV4YlZSNFZRMEtOWFJVU2xoWFUxaE5ObFJHYVc5Mk1HcHJkMUpUZEV0VlMweHJjRk5FYWs5UA0KYTBSWUwyNXlhVVIyVkZSNGNIQm1NakpLUlZjeFdHbEJaVEUwRFFwSVpqZHNRbUpWTkRkS1oyOW5OVlZuTkRKb2FDODVSRFJGVEhKbw0KTjBKM1NFZHJlVzVFTnpOelJuQXJNbU0xYzI5eE4zVXZhamRDTTJGR1RuZ05DbU5FUTJKdWFHbGtOakZoUldGVllWVlhXR0pKVlVGRw0KYTNNMWNHeEpTazlOWVhWU1ptd3diVWxLYjB0NVNGVjVhVmRxZERCRE1UYzNSR2RNV0EwS2FWVnhlbkIzZFcwNGNFSmxjMGxQY1d0MA0KV1hkMEsySjRielk0VFhvd1JUTXlWM0I1Y1M5dVNqbGtTbTE1UVVSRFJrMUJOUzlUYzFwNFNGQnhEUXBzTUZaUlUwY3ZlblZWZEZkdg0KSzA5RVJXSjNOVzVIVVNzcmVWSkVWbXhXT0RGVGRETkdlRkUyZVcxeVozcFZhR2xwTldvMVZXTmFZbmhYWW1vTkNtdzJPWG96Um5kSw0KSzNobU5EYzNPRkp0ZGpkQ1pYQlROSFEzZDA1YWFrcENWR0kwTWtoQldFMUtRVXBCUTJaTU9HcGxVMlIxYlU1Tk0xbHFZUTBLTTBoTg0KU3pSWlNsaGFlWFZGU0VnMk5GUjVVRVJvVjNNMWNuSjFTamhKY1U1TVRXMTBkWEJrU1dJeWJWRnliM1p5TDFkbk1WTkNNamxOWWpSRw0KRFFwNVdHRjFXVU4yTlM4d01WRmpOMUpEY1ZOUlZtSkthWFpsSzAwNWFYVm1ObVZvVjJWTmNXZG1UVEZGV214SE5IRnhXSGg2U1dsRA0KYlVab1Zpc05DalZUVm5kc2RXOVBZamhtV0hsYWJXWllZMjFEVGxOck5WRm1OMDFUVmpoMFVGSjNhWEJEVEUxdlZGcE9WRVJuYlZBMg0KUkdsalpHTjBMMDF5Y1EwS2RYbFRiMEZYWVRFelYwZDJaRGh6UlhVMVpUVmxhWEF4WkhjNFExZDRkRFZSTlVOUkt6QmtOVVJ3TTNWUg0KY21ORU9FbHlabXhhVUZkeldIVklEUXA2V21GNlRXRkdUMWswWTFsM2QwcDFPV0YwSzJsTFJucHdSamxTU0ZsaVNUVXhORmsxUTFZNA0KYTNkcFJHRTRlR3Q2T1VkSk1DOUtUV2RET1RRTkNqWk5aVlZ3T0doWFlqSk5Wbk5HTUVoQ2MxVmhRMnRoTUZkcWRtczBVbEZsZUZKaQ0KUjFOVk1XZFJaRmxKVm1oTVpscFJhWFJWTXpKME1GVnhWdzBLVlhoc1JEZFdXVUpXTjFCQmJIUnFUWHBDUzNNd2RrWk5ZMlI0TDNJNA0KVlNzeFJsVnVNMmczTW1JeU1IVXpWbUZpTlhGWlNFOUhNVXRKV0ZKM0RRcFdPVlpvTm1RM2VFRkVWRUZqTW5GUGVFbDFiSFFyYUhVNA0KUkdnMWQybzFORWxQTTI1cWNHOXpTVWwzVURSeGNFb3dXaXRGYkdsV2FYVm9kV1lOQ25WcVVsaEVVV2h3ZUdReGNHSXlNMDAxTjI1Tw0KYldkc1dWVkpLM3BHVmtoaGMxSnpOeTlzUzJGd2RIQTRSV3RxUVUwd2RGRnNZMnRRYVcxNVZnMEtkVlY2Y0RGV1FrNTJlRlZTWmxwMA0KVDBSTFJDdHVkVkIxVVdSTVFrRm9hMUJ0ZFU4M1RXb3lRemRwYlZGc1VqQkxSbkExUm01R1NUWkliVTltRFFwSVJIaEtOM3BMZVhOUA0KYjFCeVMwRkZVMjVLYnpKSVlra3JNRzFpYjJkVGRtdzVWbkJCY0NzeU5VbE1USFUwV1VWeWNtSjJiRTlEY1hGRWVHWU5DbEl3UTNKMA0KYWt0Tk5rUXZSVlYwVTBGaWJGaDJjMlpoZERreGNFcDVObWxwWWtKblNuazBiemxzV2sxYWJrTnpjRlJGV2tjMmJXeFJXbkJKY3cwSw0KYVVJM1VUTjVlR0ZsYjNCdVpGRTBTelpxWms1eFZtOUthSGMzY0hCemRsTkhkakl5T0d4SGRraElNMUZGVlhWTVdIcFVOVTF1VkdSWA0KUVVrekRRcEdkR3R5WVZwTFNHbDNkME5qVVROVVJUbG5aMlZ0YzBaMlRUTmpNRWRwUldGWWFXOUZjRGN4Y1dGSlduZE5VR1Y0TlVsYQ0KUTAxSlNuVXdObXdOQ20xRVRHZ3JSbTg0ZEVOc1ZVNU5iV0o1UTJWVk5XUkRNblJPTlhGeFVESlBTRXMwT0hKbVQzTTNZVGwwV1ZCcQ0KYm05eU5XUnhiMU5sVjBGbFZnMEtjakZxTjBnM1lYbFRRVk5uZFdwTFdHcGpWVlFyUlZFMVlXbHpkMmxQZDJZMlJ6VnRjVFphTldVMg0KTlVwb2NuaFdPRmx5UlZOdlZWWkxhRFE1RFFvemNYVnhTbFp2UVdjMWExQkJhRmt4Y2pCNWNtSkJUV2hGT1c5V2MzWlhjbEYzZEhaUA0KVDAxSGN6QnJhVEJNU3l0Sk1tSTFlbEl2TkVNNVRqQU5DakJDVDFaVFdUVkJSRlUzVUhaclpqSkZRa2sxSzNkaE4yUm1iMVkxSzFWbQ0KUTNoelVrdHVaemxVVG5kTVUzSmljVmhpVm5sd1lYZHBaVVV6U2cwS2RtRlhhVVpRWTB0eWVpdGxhMnh0T1hBNVFqaEZaWFZSUTNFeQ0KVEVGNWJHbHZSMnRxTjIxbk1EaENZamRNZUhkR2JUTlVOalZsZVhsNWRGSkREUW8zV2pGa1NGSnlPSEl2YVdwc1owNVpMMjQxWkZsbA0KTm1SbVJUSkpkV3NyUW5BNVZVWmlkVlpEVVRkd00yNHdMMlkxYWtSSGJEZzRSWFJyV0ZRTkNrUk1UalEyUVhCcWNXOUlWR0pHWVhJeQ0KTjNKSlJsYzVielZ6WkhOUFdrUlhlRUphVVRSUFdGaGxXR2xGYjJZeU5qRk5la2x2YkZOd1ZqRlBaZzBLZG1JMmJYWlZWakI0YWk4NA0KVTFGTlMxSlRjMUY2ZWxnNFRGVjFiazFQUTBGTlRHNXhabUUyZUVadEwzWmlkVzVZU1ROMFRISk1RMUpsU2xGM0RRcGlja2N2WTI5Mw0KVG1oQ2VWUkllWGRHUTFOU2MwaGxUMnh5Y1RCU2JIQk1RblpGUzJaTmNVTjFaakE1YlZnMlZGWjBVRU5TV201T00zaEhURFlOQ25KTw0KVFd4UGEzWktSMDVxYVd4RFNDOURTVVJyTmt4elpFc3JlVEl5Um10UWFIWnZWakpvYm1kMVdsSnNZbGcyTUZScFQwWndVa0pZUVRkUQ0KU1EwS1pVbDBVa2xEWWpadU9ITTRNRzFXVUVOcE1ETlBkRmxQWW05RGNYRllXSFkyZURKSmNsZzVXazVRVHpGSGVsSnlZM0pyUWxJMQ0KUVZNNU9GWXpEUXB2UTFjdk5rNXpWRU13UzBaR2R6TnlVR3BtV1RZNVlWVnBkbnB6T0hodGJYZHpXV2RzYjBFclQweEdkVGxuTXpWQg0KYm1keVowRmhMMjVrV2xFTkNtOTRXVmxySzJKVFREZ3lhV1Z6ZEc1SE1IRnNaMlZMTm1GeVVXeGpTemhXV25WVGNGbGtXVGw0ZVVGUg0KTm1FMGFFRnFUSGRsUVZOU1NtRjJTQTBLZEZSTFJtRnNaamR3TlhOUWMyeHpTVmxtUWxoaWNEaGtRakp2VjBWQk5TdHRXSEpTTkVoMw0KTlc5TGRHUmtUM0J6VW5GVmNuSXdXRE5pU25oMkRRcEZTamRPYm1wNFRESmxjRVkwVTA1dE5HOTBTa3hITkhSUE9FUlVaVkJuVm1KSQ0KUzBwUFZFSXpWVEZ0VDNwSVJ6azFlbU5rU21aR01USXpiR0lOQ2pGNEsycGxPR05aYnlzdk1tSkdjWFpUTlVaUmRqSllVV3BqYWtORQ0KWjB0SksxcGtTR2xWU2pobVMyVXhVMmcyYkhaQ05YUnhXa0pyVVU1RFZnMEtkV0ZSUW5wVVpFRmpjV3AxVkVRMlZ6aE1RekE1V205Ng0KYzFsU2RGZHFTVEpuVFZOUU5FdDNRM1pFUlZVeEwyc3pUVGxsZVZoMk9VNUhlazV1RFFwSllXOHJlSGRMV1V0V1JGUkhXa2xSYTJKNA0KUVhGUWJ6RkNWWFJIU1VFNE5WZ3JZbW81ZERsWGFUVlliVXA1VW1ZMFNEZFRUaTlWYzNwamVHc05DbXBaVDA5NmN6UldUMEpwVEdGYQ0KU0dKM1ZVdHFTelJpWld0eFMyMW9Sbk5QTldWMFVUSXhlRGxHTVdKM05tY3pTalZRV1VsRlVYaEJhMUpyTVEwS1MyWkNLMmx1TWtKWA0KYWxkbVZIRlVSMWxhWkdwd2JWZHNOVXBtWW5wc2VuTjNRMU5FZVdObFJrUkVSRTlLZW0xeldFNTZRMkZVZFRRMVpVOUJEUXB2VjBaMg0KWlhGbk9YbE9TR2RsTTJGeVZHMWFPRVptTDNoakwxSnBVMGN2TjB0b1dreGhjRnBQYUc1SlQxaDJjM2N4TkdKVWVUWXlNbWxZSzJZTg0KQ2xCVU0ybHhiMVZIT0VoUVRHNVNXRmQzVG5sRWIxTnRWMWxxWmpSTU1YSTNiMnN2VkRWR05uTlpXR0ZuZFdwVU5DOVNjVzVPZHpoMA0KVmxWNGN3MEtWMlJDZUd0VVIxZFBiMDlpVUU5ek9VWnRkRlJuZFZWbmN6SjNTakY2YWpWdGNVZFhVM0JSTjFaUGJYcEVOek5XV0VWdA0KSzBGQ05sbDBhM05FRFFvNGEyWkVaRUYyVlRWTFlVOUZhSGhwYjBGSWIwVlVOVXcxVG10bFdrUmhWekpqTlRkMk0zWktTR05RUzBadg0KV1ZkdWMyUTBTWGxYVGpKRU9EZ05DbUl2VDI4NGNuWnRUa0V5UzBzeEsxbzVaMUZvVlV4VWRXaENUVGx5TlM5NksyRkNRMnBhVVU5Vg0KVkZSRldIRjNVVEZYWkRsbFdFZHpXblJTS3cwS1dUTlRVbTVSWVRONE9HODFablpIUjJnd1UxQTNaalEyVVZoUmNsWklZamhUUVRsSQ0KV0RGbFpreG9OVlZXZWtOUmEyVlpSRWN6YmxSclF6QkxEUW94TWt4dlZtZGtTVVJ3TUM4d00zVXhLM3BvV1ZwSGFXOUZWakpaTjBsdw0KYkZwb09GVk1kRVY0Uld0bGVGaFNUREpXSzBNME5sb3ljMGcxUlVjTkNsbG9iV2g0UmtONGEzcG9Vamh5VUZKTWNYWlZSVFZxZWxwSQ0KYzB4alVuRjNMMlZTZUVaSE5USkpObE16YjBwUFRtbEhPVGsyWWtsa2IwWlpidzBLVTJWMFYyczBjMDg0VTBFeUszcFRkM05tVUdkRg0KZW5kMVYzUldURGhWVmtOU1N6UTNiR0ZVZVM5dk1EaHpOaXM1UlhsWVN6RXlMMU5tVTNneURRcFFNR0ZCUkRnemNFOVNNbGRwTWxVMw0KYkVGVk5YUm9XVXgzUnk5bk55dExTbVpzTVhaV2JWQkRTREYxYkROU1RFSnlNaXR1THpRdmIzSndZVXdOQ2xobWJXVm5XU3M1ZUhSaA0KWlhkT1pWSk1aMmt3VWpNdmNtSlJkVWcxYjBwR04zRTJNME5NVDJjM1JHVkljRlpEWkdod2RERnROa3hpVEUxMFJRMEtZVEE0VlRsTg0KUlZKWGNVbGxWa0pWWmtocFZsSk1ZbGhrVVdkNVVuZ3ZXa2hMY0ZjM1duVlhRV1kyV0ZoNVJEZDVkR2QwWkZCblJEZEVRMmhWRFFwVQ0KT1VsR2EyNVRLMjQwU21Kbkx6azFOUzl2WjAxa1FXNHpjRFpQYzNBd2NXWTRkRVEyTXpWVlptRm1UelJNYTBaaVRYZGpkVmxTWW1KVg0KUm5FTkNrWkdVV3hZYURWdldYSkZWbGczTlZGblFVOXZUMDFCV1RGU1RHdHhlamw2S3k5dWMweEVUMWQ1T1RZMmNXbEdPRGRMWWtaaQ0KY1RKNmFWWXdlUTBLVGtKNGIyRjNiRnBQUjJSa05FcDFjVkppSzBWR2FWWTVUeTgzTlhSM1dXdDNZbE5tTjAwM1YwZEVRbVkwVEdOVA0KU3pOVGEwSTFhM0J6UjA5SURRcHVlVFJIUldreGNtd3lVelpvVGtOTFVqTktVakpUTkZkRVVtNDFWRXhoZGpCM2VYWlJVamxKVUVacg0KVDFCbVQwYzVWRmxwY2paWFpITlhPRFlOQ21VelJIUjFiV0kyWlc0NE1FOXFPR2hIUjJkUWFHTjVlbEEzY2xWU1VtNUplR2R1YlhaQg0KWkRaM09EVlBjRzV3TVhoUmJFNUdPQ3RWYkZaVldnMEtMMDB5ZWk5SFFXcE9hR3hCVGs5VU1uUkVUMlJTYm5ac2EyODJNVGxPTjNFdw0KZVRJM1VIUlFSalZVWjBaNWRFSlZUR3c1ZGxoTk5tMU9SbmRIRFFwUU9VMU1TREZCYlcxNllVVmhUVnBDYjFsVGJYVnNORUZoVVRGRg0KT0ZCYVZrNVBTM3BpTW1Wb2NqZG5XRTlpZVdWelYxcEhNR0ZrY2xWbWNqVU5DbXg0VDFscVZsWnpOakJhVmt4c1pWTmtaRFJNWmxGaQ0KUVU1TlVGbDZibHB5ZWpndllVOTFkVWxTZG10cU1rMVNOMnBxZGtwT01tcEZLMGRNVkEwS2VHVlhTVUZvZVhac2NWQXZRMVZMWVcxeA0KTTJkRVdVRTNhRVpPTDBkdmQwbHRORk5GYzJoQ1dtNHhVVlkxYlRadGJ6ZDNZMHM0VTFwemFEZE5EUXBoYW0wMVVFZ3lXalYwWXpSeg0KZG1Sb01XWkRSM1prU0daSE0yZDVRblJtV2twMGJUUklWM2hIVDNrM1pUWjBiak5qUkVFMFpYSnplVEZEWjJzTkNpdFRTM0pZZERrdg0KTW1WNFRXbEdVelJEY0VOU2NsaG5NeXRJWlVKRmFVNVVaMmxVTkZabWFqWTNUMmhsVG1jM1JtUklXbU5vZUdkR1ZqTlNRZzBLV0dKSg0KZUVKRVVEQlhPSFZ3YUUxT2R6SldWa2RvVGpKYWVISkRRa1JoUTJWS2RYRjVZVGd6YVhCSmJFaEhZaTl3TlhaVlJqWnZRMGxPVDBSaw0KRFFweWNHNUtaV1JaYVdwbVQyZEtZaTlZVUdsT0sxZzFWRk5oV1dkaE16RmlhbmwyYnpOVVoweFBVbFJ6VUROaWFXNTVaa05yYVRWeA0KVW5SWlUxb05DbFZtUldwb1ZEWlFTVkkzU2toTFdVNVRVR2hEYWxOMlRtUTVaRWRRTWxFelNIZHNZM0JLUmpaRlJYSjJjbk5XY201UA0KZEc1NVVsWktORGxIVGcwS1JWVmFVRlJRY0NzNGFqWktlbHBUTm5kNlUwaFdWVVJPU0dVMVFtRTVaRGxaZERObWMyWTRkRlpwWlUxUg0KVVRnMU9XVlBNVkZOVVRGVFVWbHdEUXAwTTA0NVNHdFlhM0o2UkdSM1VtVnJTRlZrWVVKWFVWcHVLemRTYUZZNGFIZERkblZUUm04dw0KWVhCVFVWUlZWblZ4ZVRjNVJVTjRSSFpvU0RnTkNrNWpRVXA2Y0doSWJETTJZa1ZhYUN0aVowVkJRMHBGUmpSdU1HcEVkV1I2U2pKSA0KWTNoalpWZFNjVUpIT1cxTFRsTjNTRzUzZEZwbk5tbzNTdzBLWml0dU5WRkJhbUp6VjFCTVdtNTBaMEptTUhKSlNFTkZNRFpCWkhCeQ0KV20xRE9VRkJabHBVVXk5SFVqZEZOMmhxZGtzd2VtWXdWRFpPUzI5NURRcE9MMUZaWVhRck5uTmFNRXBXTHpsaVZIVkllVVIyTlVKeQ0KYmxKWFJucDBkbW92TjFKd1ZYZFFXbEJ2U1ZSeWFVRjFMMmd3WkRsd1FrRkhiRllOQ2pkSFdVdDNNM3BTWWpkMGNteEljRlZQUTBneQ0KWlRCbVluaFpjV0Z5TlVSVmVWazVla0p6UmtSWlVrWTRSRTh2WVcxdU9GZHJibUZsVW1kblRRMEtiRk0wZGxsRFlTdHdSVk13UldKSA0KZUhveU9HcFJWV3gzWW5KNVVFZ3diV1o1T0hweVVqbE5jVUpUVUZZd2VIbDJZa3cxYjA5dWFqUk9TbnAxRFFwYWFYUTNNa2RrUkVadQ0KVkRSbFVUTTJUbXg2UVZONU5YZHhZVTlpSzJ0SllURmlkRzg0VEhOeFdWRXdkamhYUzJsdFMwRkRkRTh3ZVVkVGNsRU5DbVozTlM5MA0KVjA4eFZXWmlabFJzWlZaM2NHZHlOR1E0VEV0cFNHWnViMUV3UjNwdVZXOUlha2wyUmtKb2RrcEdkelpEU0VwelZITnpMMDUwT0EwSw0KU2xOdk1sRlpTMFo0VWt0Qk4ySkNlRGxTWW5KdGNsWnlORWh5UTNKR1lsbGFkREpIU1RadVJ6YzFaV1IwT1hGUGNtVmFNMUJoV2pGSQ0KYW1NMkRRcEJTMFJ0TkhaQlQxbHFSMlk0VVZjME9DODBjemMyWVRZNFEwNVZla1ZEWTAxU01YTXpNRlJZZG1sR2MwOTBOblZ4Y1cwNA0KTVRkUVpYWmhSMVlOQ25Kd2FXUXpVRTU0Y0RNcmMyZHJkRmhQWVU1clEzRnRiazEwTWpGRGNVMXdZVk5ZYjNOTlZWWnJXWFpoY0ROdg0KV0hOd2RqVTViekZpZDBjM2F3MEtUMVIxWlRSdVlsQkRiM0JHUVVGdFN6aENZbGhCWjBoS1NESXJibXg0Tm5sUEwzcGxkWEZOU204eQ0KV0RWRVIwMDJZbWxZZDJWTFpITlNUME5xRFFwcVVrNVhZekpIUXpKNVQxQnJlVTQ0VlRoeGExbGtPR3d5VG5GRFdUbEJNbmhyV2tSQg0KVVRjekx5OTJXVUY1VURsNGIzaGlWRUpWYkdKbmFVWU5DbkJ0TUdjclkxQlZXRXd2YWxZM09HZEZNMll4VEROT1UwcFNia3REWW5oRQ0KU1drd1VVMVZOM2d4ZEhaRk1IbDFZelV3TWxoTFl6TnFWamxpVGcwS1VtZHVlaTlwVlZKT1NDdHNRekpGVjFCQ2R6SlBjRTRyT0hKVw0KVTNSVVUyVlJiVkJFWkhST2FHeFpVRFF2VFZGd1JVSnFSWHBFT1haWlpTczNEUXBuVFRFMWJUSjZabkpZU1RkYU1ERXlWVWR3UmpsdA0KUW5KQ0syVjZkSEYwU0d4TFdIZDVSM0ZhTmxocmQyOVVOWG92VlVsb1RqaGpOblZKYkVrTkNuWXdObU5ETW1sSVoxWTNXR0ZGU2k5aQ0KUmtaelVUVlBibm96UjNkaFRWYzNkbWxPVUdKWGJubHdVa28xVGxCYWJGcEZhMmhIVWswNFVESmhWQTBLT1dodlEzVXlhMVZOYlhGcA0KTTI1Wk1ETXhNbWsyZVdKVE5XbDRWbVZXZWtvdmRucEJMMnQzSzNWdGJXczRNazkwUlVSMGNUQXZhRkl2VGsxRURRbzNlbnBDV0c0NQ0KTmpGS1VrRjZZazUzWWtVeGFYZzFja3hxVmtsVldVUXdOMGh6V0ZGcFMxQnhjbTVPZGk5Q1RGZFNjbFpFTTBsMWVuSjJlRE1OQ2tKRA0KUjJsU05YZHljVkV3U1hkeWNEWkVaVkpVYjJjMU0zUnZWVTlrTm5KWE5EazBZVU5XTm1scFIyODVlRmt3V25aemNsVXlRMFV4YkZwYQ0KS3cwS09HaG1jRUZDZEdwb1Ewc3pVMkpPWlcxemJHVjNVRFpNTjJ4Nk9ESkJhVWMwZEhWSFkxWmpTVzVhTUhoTFJWTTJVbkphVFRsMA0KYTBsM2VGaHhEUXBHV0d4U1QwRkVTbWRqZFhoMmFuVk9TbWQ0YjBRd1JWRjZOUzlCY3pOUGFsQnFORGd5TkVsaVJIZG5hR2x4UlRaSg0KYWpJNWIySmxVMHhsTkV3TkNtRTFiRGxIUm5SRWJUZFhZbEZ1TDNKVVkxaENWRFJJWWs5SE1VSklXamhSWVRWSmNFVnBlRTVHY1RkbQ0KU1VKcWVXeEVTRU0zU1VWSEwwSmFXUTBLUWxkS09HUTRTM051WkV4eVJqTnhXRGQ0YVZkc2VEZDVUVU5xVTFadmExWkViVE12WjFsNg0KSzFGWmJVWkNTRk40Y214elFWQlljeTlSVUVkNERRcDRRazlQZDJkQlJURTJWRlI1VGxrelFVTktia1ZrYTJnMVREUXJWRVIwVnpSRw0KVGxkUVJUVXhNMEZMVDIxbGNDOXhNMmxZVkU0elZtVktSRElOQ2tWdWNVWnlhbXBOVTJkT2NUazRTSE0wU1dOa2VrMHpjVUpNYUdsWA0KYm5wcVVtcGpSbk5GU0RGcFFqRnpkVUpoYkVOb1ZFdHZRMVZWY0ZJeVNRMEtiME00WW5sUlJtbGhZaTl6Y0ZFeGNFeERPV2xZYUdKNA0KYkdVME1HODViRE5vVWxaSVVsTlJMMWhMTm05ck9FSm5kamt5T1RaSU5sZEJjMHhSRFFwWFVEZG9iV2hzUkRBelpEUk1jalkyYmk4dw0KWTBnNGMzazBWblpNWlZCM2VuVlJPR2xNUkU1bWNYZFBNbEJ6VnpVMmFUUXlRa2h3VEdadFdVa05DbXRYYzJreGFuSkVWbXRDY1ZnNA0KYW5CcWFqQXhOaTlRUkdSck1IVTFOR1p4Tm01S1ZpOU5jSE5TZEU1V05XaFZVakJ1Y205VVJsbEdjVWxQZUEwS01GUjRNR0l2WkRGSw0KYTJGbU0yMHJTWFJoY21wMFVVOHJNVmtyTW5jNWVXOXhNWHBDUnpKVEwzWmlaWEpvYnpCbmJUbE5WRmRtUm5STk5IUlJEUXBFZGxOdA0KUkdkSFZXaEtVWGNyTTJ4MksxaG9jbEIxWVU5dFVsUXJRVFZXZURCU05WZFFMMlJ6Vm1GWU16UnZSRkZLZDBod1psWlFUa0kwTVhjTg0KQ2twcU1WQktPWFJYTjFJM04wRTRLMlpqY2xKd1QwMUpkWFJFWnpaWFRYaFdMMWhFZEhoc1F6bHFaelJRSzB0UFRtYzJaR1JRT1hsUg0KZEhSWGJ3MEthMWR4VTFJMWFuTnpSMlJ4T1c1dFMyNVJWRVoxU1RZdk0weDVVUzlOU1Vkc0swdEJWSEY1Y2sxaVVVVkRNbTFUY3pOTg0KTmtzeE9EWkxWbXRqRFFwbFVUQXdNazh6T0V0a1lWbE9VMFpwTDBjck1YTTFiVVJ2ZUU1UlNDdG1VREl6Y0RaM01XVlVNMDh2WVRNNA0KV21kSVYxVmFUaTlEYUM4eU9ISU5DamxoTjAxNWNtTnVlRmcyT1haSFpIRnZTbFZFYm1aTGFHNWFPSFl3ZDFKeFZGaEZUbm80VVd4Uw0KUjJGSFoyOUNNV3ROVUZwalRHNU5jbVZOWncwS1UwOUdRemxrZG5OVVoxcG5PWGd6WkVvM09WRkhVM0pRVVhCRUsxcDZjMjVWWldJeQ0KTXpReGFsQlhZWEpVTVRGeFpVNHJkRTh3TjI1WWVVTnJEUXBGTDNkaU4yeHpUMUZ2Ym1STVRGUk5aVTVFU2xBeFNteHdXVFV5ZG5CRg0KYVhsNVYyUkVibkJQV1dsb2FqVndURUl6ZDA5alJuQmlieTh3ZVVjTkNtaFNNMjF0TWxvNU9IcHNTakZuU25GTE9FazBjRTUwYzBkMQ0KVGxKb05rSjJla0l6YjJkUmVtVlFkVlV3ZFVWVVNHZEtlV2h3ZFd4cWJuUkVZUTBLUlVZeWFGSnNTekJVUlc1NWFFbFJPSEZsTms1WQ0KVjNwcU1VMW1SVkJEUm5SNk1YRnRjV2RQVmk4MGNHNDRMM1ZZYUVJMFVqRjZkbUZpZG5OMkRRcGhUVXh5VnpndmNYUkpUV0ZFVGtWYQ0KYkN0eFdEQllOVmx0YjNKSE1VcHlRV2RCY25GSVlXWjVjVGROWmtwNGVsSjBNR0pHTVc1TWMwbG1iamtOQ21sTWVITklTWE53UWt4Vw0KWkc1alVWSldjVVUzVTI4emNrUk1hbk5MUTFaNFNXODJhMU0yVkRkUlRYaE5kU3N3ZG1vdmNuVnVXakpzS3padE13MEtWSFZwZUc5Mw0KU2tKdE5FaFZTVkJUY21aak5ITTBRUzluV1dWNU1YTXphRXBOTHpONWJFOXVhVnBGU205d1N6aHFiMk5pVkZneWJGTkZaa1JHRFFwcg0KVTBOUFJrRmpkWGczZEVka1dXSmlNa2hLUzIxSlptTXZWMUJ1ZGpBMllqaHROVTgxWkUxUmVHUnpSRkpHVVU5b1RVVnBjekJpUlRWRw0KUWtZTkNrbDNVa0ZaU1dGb09GZEJVRlUzVEdKUVNUWjJSelYzYVZKMlF6bG5SbXg1UlhWeVVqTlpWazVXU0VoR1FrWTVZV05UWW5OMg0KUlVGTmNsQjZUdzBLZW5GWE1FOTFZa2hUYUZwRmRUbFNTMDFJY1hOdFVqRlhRbVpWV0dsa2VVRnNNWHBSVTFORWFGaDFibEZoTmpaQg0KUWxkM1ZEZG5ZazFDU1NzMkRRcHFURk42VGtKWlUzYzNXWEl2ZVhOa1pEQkpkV0pTV0c5b2J6RmFhRlUyTkdReWFreHBkMkpUYWpZMQ0KVTJWdlVtVmhWbXRoV0hkT2FqSTJUR29OQ2pSNmIzSk9lbUpsVVRoWVNHUTVMMFpGTml0WWJsRlJWMDVrVld0Rk4yeHBUMk0yU1ZKVQ0KYkRKSVZWY3pkR054VFRaTGRXMUhZbEVyVWpsemRBMEtabmhPTTFFeWNEVnJkblpVYUVkUlpqVjFOMHRNV1cxWlZUVTRZMHcyYVU1Sw0KV2pGMWQwZG1lVmh6VEZaWE1qSndjSFJZYm5kWFFVSXZWalkwRFFwaVpscE9VVFJpZVVWVk5GcEJWbk01T1ZBMFVHSkljazlWYjNoWg0KVmtOUE15dExWaTgyWWpGMGNuaDBiWHBrVFVwWWF6ZElTU3MzTUdJM1pEVU5DbWhLWTNGVlltbE9UMmwwZDJWMGFrWkJSRWhSYVdWQw0KT1Uxb1pTdG1NRzl5Y1VwRkwwRXJaRk5aVnpGRlRHcE1XRXB0VWxCdWMyczBWRzVHU3cwS1EzUkdhVXh1VkVWbVdEaFpOVXh1UzBNMw0KT1doUWRtNXBVVnAwWlVKcFVXeDJVbTl0TWtwVmVHMVNjemxvV0ZwR2JHRjJiWG93TjI1Q1RtUnVEUXBMUzA5emEyZFdaa3hJV2pKeQ0KUWt4RVIzSmhWV1ZxWTFWSk0wdE9aamxNVTFCb2FXWlFRMUJGVW1FelQyZ3dRVkJLWmpFdloyUnJTRk54VWxJTkNtSlJZWGhNYlZodQ0KYVVkT1pFSTVNVmxJVkc5dlVqZG5UMGxoUzJ3MGFtaE9ha05pV0Zkb1lXczFWalZRVWxKV1RUUnRlVzVXTm13eVdUZG5jQTBLWjJOdg0KTTNsR05VdERaa05UV0d0UlZuVjFUV280TjNvMmRGRk1jM0JvYzI1V1NXOVNXVkpEVUZwMWJrMDVObGRJVkM5UVEzb3laMFJoVldweA0KRFFwclFtTndPVmhJTDJaYVZsZHpNRWxuV0hCMGRqUk1WVnBoTkdrd2JteG9Na1p5UkVkTmVGWjNlVzh2VVdSWFdFSlZhVXMzTlhoag0KTm5salFtSU5Dbm8xVmtKWVIyZDVhREI2T0dkUlkyZHNMM1pyYXpVeVkxRnVZV1kyWVVWMVVrUnJlVlpGT1VSM01tTmhlRk5YWVZSRQ0KTURKNFVUQmljekU1Y1EwS2RrMXRXRXAwUTFSUE5VZFhWRTkxVW04clRtaFBhVXBVU1hkMFpESlVjQzlMYVdvdmJ5dDJUaXRLYnlzdw0KWmsxNWVVUjVjRms0VW5oa1ZGbDREUXB0UVUxV1JHWTJja0UxZW5kSGFGWnJZbFZ6VmtWM01HcEdhVFpEWTNoQ05HMHljV3g0ZUc5NQ0KWmtaUVMzTkxabE4wT0hKV2NXcDNNMGt5VFdvTkNuVkdTMDFFZEd0UlFrUkZSekJqWkdVMFlTdEJWa1pPWnpGUGJYa3lWWHAwVERORA0KYlZNMU9VMDRhM0ZFVjNKVFNEaGtOR0ZwVVc5Tk4wbGtZdzBLV1VSR1NEWjNOeTlvZFdacVJIVkhObEZ3YmxkVlR6WXJiVVJZVGsxWA0KWWtWVldTdEpTbmdyUmtWaldrcDVWMFJZZWtnM2VWbE5SMlJYVEVNM0RRcFphMmgyVVVoemVrdEtNVmhsTDJKU1JtNHhOVGd4WkUxVA0KUVVGNmFXZzJTMmxRTUVkQkszcEpha2hRUTA5eGMwbHJkVTVVVFUxT2FURndhMElOQ25oMVVGVndORVZYY0hwRWVXVlhhRmxVUjNoMA0KYnpoa05EWlRTVTVYTUdGdU4yNVhSbGhOWmpoQ1pIRk5TMGRVZVVSYWQwbDZkRWRNU1RsbFR3MEtNelJZWmpsVFlVUlJhMnRYZVhkcw0KVW0xamJtODNXR0ZCYlhsc2FuTnNhWFJGY2xkbmJEa3dhbTR2WnpRclpuUjFNblZ2WkRNeWJYWjVXRk5WRFFwb01Xa3hOV3RLYW13Mw0KUVM4d1Z6ZHlkbmxFYURoMFIwaGtZbUppZDJkUlZ6WjJNR2hxWTJOa1RVeFRia05vUTNsSlVGZ3JiV1EyVDNsbGNrOE5Da1JNUmpWVw0KUWxNclJIcGFOMUV3SzAxcFUxaEpia2RLUVZKelRXMVNURVZCY0U5TlpEZHVkbHAxU21aWU5TOHpZblJtVDBGcmRtUmtNVU5CY1EwSw0KV21KMlVUWktkWEpUWVhsU1owbFJjbFIyZFZrMmMxUmxZamN3YUVWMVpXZEVXRFZMYW1SWlJHRmFVemxrWVZGME5rSnVRazlwVTBoRw0KUlM4NURRcHBUbVJSUzJZeFZuTkdSRlFyZG10VldUbFBXVXRPUVdWS2VDOXdSSEJHWkZSSFYyWk9jRzFGTlVreVpESkhVekJuU2t0cA0KT1ZCNk9FVjBZbVVOQ2pScFpGazVkVVJUTTNaSGFuSklja2R6TkVaT2VsbHFXVXRwT0RoM05qTnZPRFppZVN0RVdERXpjVFl5UkRaNg0KUTNGV1RqRnVValYxZGxSMGJ3MEtMelpYYVRKd2VrZHFhbTVPU3pWdFFsUXlSUzlWVmt0RVVGRlJVMjB5Um01VGEzWTBZVE01U1V4Rw0KYURSSVNYQnhhemx2ZVU1R2QwdGxNM3ByRFFvd2JHNURVazFwTWxCS09VTmtUbGhJZEd4TWVISXplbGxCZDJWRlRVUk9WbEozT0RWNA0KWTNWVVNtZHVZa3R2ZUhGbFJuUTVTalJUUjBac01Xc05Damx6T1RGNVpXeE9TVGh1UzJ4MmJIY3dTbTh4WlV0MllXMDVNQ3RrZVZGag0KUlhveGJXWjNSWGhHUldoRVpDOUVaMEpZVEZrMlZWb3hjamRxY0EwS1VtOU9kV0U0VG5Cd1FYWXhXRXRSZEd0NmFuRkxTV1ZoV2xaSg0KTWtrMWVsTXZlVTEyT0ROUFoxUndaRkJMU1hZNWNXMU9WeTlNV0ZNd1puWlhEUXBQWVRobVdIZ3lRMVpCYzBOSGFGaEZORWc1ZFhOdw0KY0VvMFF6TjBiVlZEVW1welJuWjJla1JDUVhkVWRFdEVOMFV6ZGpKeUsxazNSM1U0YWxNTkNtNXlPV3BoYkVScmJYRnVkMGd3U1VORw0KY1d3NFNtUkliSEJDYjNGaGQwVnpka0pPV2tKa1EwcE5XVFpLVmpReWRsRm5aMUpZZUhoM1EzbERjUTBLWWpoYWVFTXJhVmhGYXpGVQ0KUVhWS1VWTXdRMjlQTUZSU04wMVRTbEp6VGxoUWFXOWplV1Y2Y2trMldtaDJZbkpuT0ZoV2VGQm5ablJzVldNNERRcFZNeXRSVkd0SQ0KTVhGaFFuVjNhbGRPVW5NMWNWaFdTbTh4SzNKMWEwVkVkV2hOUlUxRmJtZDBhVU5yTm1WNllYZGpWMGQyVERKc1pXd3pRbEFOQ2tJMQ0KTjFKWFMydzVNRzFRYUhOWlYzSldkVkZPVkZvNVJHTlBWR3QxYnpKSmVXVnVXbXBVWVdZNFpXZFlSR0puZFd3elYxaGxWa055WmtWUw0KTXcwS1VXcEJPV1EzY1RZMFZXVkNVRFZ4ZGtjeFZtVTJTWE5PT0VVeU9XaE5WR1IwZFhoeWNuWk9OemxqWkhKemFsQkJSVU5FTVhsaA0KWms1bFRVdDJEUXBJYkZkc1p6YzBhelV4TkV0a1ZFTnhVa3BXUVZFNWVqUjFiMDEwUWtrelJESkZMelE1WXpObVRHZEVRemhrYkN0VA0KVldsNFNuaHBSbTVMWVZnTkNsZzJTV3RSZWs1d1Iyd3ZVRU5PVjJKMllXeGFjVEZuVUZaNE0xUkRjMVpsV2xGbGVERllTMHg1VGpGRw0KYTBnME5FZGxXRlJhWldoR1dqWXhUZzBLUVN0dFdFWXZkMGxETWxaWFNVWnpOMU5rYVhkVVExRkJiVWM1TTBoNFFWUlpibTlyWkdSQg0KUldneE1YRkZRVkl6T0Vsb1RGUk5ia28xYjNwTERRcGhVMHhLVUhCM2FIVk9jVnBUZVhWdmJuaGxWVGhDWkRWbGNVcFNkMlJ6ZVVwTA0KZEc1bFZrOTNUR0p2TW5ORk56UTBSVkpTVUhabFkxTm1XWFFOQ214RWVXSlVkSGRDWVhjMlkwTnhORmh1TVdSdVpVMUZlV3M0WVZkMw0KTjFWbWFHaE9VV3MyTVVWa2RWbEtRWFozWWxsa1MwaFdPVFIyUWpaeVVnMEtVMWxOZDNwSmFHMTJVelYyZHpSbmFsUndPVGN2UzJSUg0KY0ZScFJFODVUbFl2UjFsTlQxaDZWbEEyYmxWVVUxTnFNa3RIVkVVMFdWQjFlbWxuRFFwcVdGa3dUVFpYY2pGeFdqUTJTMEUwWkZkeg0KTVhkM2RGVkllVXBqU2tWYVRXd3llR2R6YjNKUlNXTnZaR1JvYjBGRFUwOVZXbWQ2Ym5aMmFrb05DblUySzBwUVRsTkNRakJGV0Racw0KVml0ck1uZGFOR05oYTJ0ek0ydHFabFp2ZFc5T2REZFFSekJsWTFaelZsTTRjR1pzVEVkVlNrTkJlRFpaYmcwS1lWUlZTMkZZUmtKVQ0KZUVOT2NVbGlaR1J3WjFGNWVVdFNkbXh3T1hsQ2VWRlBWMnA0VDA0eFYzZzNkMHQxTmxwRFlUVXdRV1ZzWWxCbmIxZFhEUXB4TDFKSg0KTVRGMU5TOVhNVTFTVGxjMFYzcHlWamx5ZUM5SWVXcHZiMDkxWWtwbmRITjRUalZ5VlZCT1RtWlJlbXhKZFc4dlFVZEZNWEJIYjBJTg0KQ2tOT1NrdG1NVEZYZGpGNVZYRTNiR00yUVZCMFQyZDBSM0JzUWxoRFlWaEtXVkJyZDFrclYwZ3lhWE0xWjAwclJ6QjVVWGxFWVZOdg0KUTJwV1NBMEtOa2xoTURKbWEwRkhkVzlrUTJSS0swVnNSREl5UlZOWVlsWnlkbFEwY0hSbVNFWXJhVGN5VTFwSE0zbzJXRFk1VW1SSQ0KVmpOelkyOTVXa3BaRFFwb2VrTnllR0ZvYW1oelZVOVVOVW8xTURKYVEzaDRTM3BqVWxsaGJFdFFTMEZUUm5JNVRWSkNMMGQxTmtaUA0KVjJwMGNWcHdTQ3QxZGxwcldGb05DazF3YTJSSFJUQmFVSE5zU0ZsV2VXOVdMM2hZZUVsbU15OXlVMjVoVlUweFZGWlVjR1ZtU2psSA0KT1VaYVNtRjNNbE5xUjAxelV6Uk1ZVGxMVHcwS1ZFdDVWM1pZZFhOT1JHdEJMemRETlZodE5tVXZVVkJQWmxWTlp5OUNVbmd4TlRobQ0KZVRORFVreGtja3NyYkRkSFdFZHhRVzlhU0ZaeU9IQjNEUW8wVVZkVWMzcFNWbXR3YWtSNlRuRmpiVTFMT1RaUlFrWk5UMHQxY1VOcQ0KTUdSMVlUSmpOWEJaWlZKMU1sbFRhRXc0VUhwUU4yMWtSbFpXV1dFTkNqWlBZVWhUU2tSNGVWcE9PV3BHTlUxRmMySkhVRFJuV2twNQ0KVTA1c2VuSmxRbWx5WlRGck1XOW1aWHA1VUc0eGNHcEllVGRYU1hSTldscHRTQTBLYnpScVJ6Uk9ZbVZZYzJGRFJYSmpNblZRT0ZOMg0KZEVsbE1UVjRXWEoyT0djM2JsTnZNMlYwVkZsblVqQTROWGszY0ZWWlYzWjNWRXBUT0VGNURRcGFVVWhDVG1kTlVtOHdaUzlwTDNaWA0KT0VGNmNHUkxObWc0T1V0VkwzQTBNM292UjBsaFkyRkpaQ3N2VDJWVGVYSlNSa3hZV25BeE1FaEVWVlFOQ21GU0wzTm9lbEJPWnpaTQ0KUlV0dFpYaGtTbTQ1VFZWSlVqaGxjQzlyWkVvNFJtRmhkMVJLUW1OSk16ZHBiblZ1WVdaeWEwRnJWV3hTZVhoS1RRMEtSVGgyVHpoMQ0KWkRoalFXeGxia0oxZUZseGVWTmxaa0paVEV0SE4zUXdMM1pyZW1wa1ZVSlVRbEo2VkdsYVoyaGxXVkJCTDJvNGJTOXBVVzloRFFvMQ0KWlhNMFEwZHljMGxQT1djclZFZE1SR3hxUVVjMFpsVnNlRU55YlRaT01GbHhhVzk2TlhoRU1sZEJWVEpKVDBkWU9HRk9jbUl2TDJFNQ0KYUZRTkNtVjJWMVpDYTA5d1MyRkhkR2xUTUVVeUwxbHNjRWt3ZGxoWGEzcENLMnRZZVdwWWRtb3haelpVU1VaV2FXNDFSWEpaWjFsUQ0KZFhGclpXZ3JlUTBLZVd4eGVGUmlaM0ZUYzAxRGJ6QkJiSGczWm1ZNVpURXpZMHBNUjBkR2RqRTVOMjB4Vnpkd2JGVkVZbFZFV2lzcg0KUzNsWVQwTjJPRWc0ZVVzd0RRcE5Ta2h6TmpkU1FVWk9WSGhzTjBkSlkweDJVamhDTWxCUWVXOHhSM1p2VldWMldYSmpjMWMyZEdSSA0KTDNWcGRWQkRha3h4UWpWNlZrSjNTbmdOQ2tNelVWZHplVkE1T0VSeUwwTkVRamxhZEhwMFFYRXplRzh6U2xwU05XVktiREJLZUdSWQ0KU1ZsSVVGRk1RbnBrVjFrMFJtSm9iV0pzWkRkRGR3MEtTSEZoVm5STVdVNUJaVmhoV2pBeVRHaHpTSEJ4VlhSUVlXRkVNME00VDA1bw0KUW5SVVFWbGhhV2x6ZGtGWFVXOWxMM1JtTUc1UllYZzVjRFZ2RFFwRFdqTjBhRWhPWlU5aFNWZFVRblptUkZsMmJrUllhekp2UlhVdg0KT0hWUVVXNXZaVzFaVFRWb1QxQlBPVXAzUjJORE1XZ3ljMDFZVm13M1dYY05Da0ZIYldod1RFOTFhV1JqV1ZnNFltVmlNR05EWVdWTw0KWkZrMk4xUnJUakYxV1dvNFUxTlhOVm95UWpWQ1ZWcDJRMmhyVDJGSFZIRnpNSE5uTHcwS1IxZGtaemxITW1SWE1saFRaV2xDWWsxbw0KVEUweVVWZGpjM3BIWTNNNVF6RmFORmhDWkdwclpFWjZiVUpYTDBwRFJuSm1aVTF5Ym1wVFZXWm9EUW94Y0ZZNFVISkNWRVZIUmxKMw0KZEVzMFJXcEhUa2REZFRGQ1ZGbFZWemRwYjNnMlQzZzJaVTh6VmpOMVYyWndMMlpYVkN0VksxSTVkMmx1Y2pFTkNrcHlTRzVyU25nNA0KZFZCSlUwWnZiV0V6YWxsMU1EbDZWVzkzY214aGFIZG1jUzlTT1U1aFRrWmxWRnB4YmtkM0t6RjNaR2hSUTNwWk5sTnlidzBLVnk5dA0KTmxrd2VVWjJaalp2V2twUE16WjBUa05SZVhoTWVVWkJRVXR6Wm10NGMwRjJTMGwyTkc1T2VVbHFibkpLVGxKUmQySnJaV293TlhWNg0KRFFwdE4wMVRhV05ETjFSTk1tb3ZhMlJTUzNjeWNYTk1lbWN6WmxoaWVFTjRXbVZTZFRGRlQyRm5helpLT0dWdWFHVk9URmg0U0RaTw0KZWtoNGFUUU5DbGR0TXpoMVpVdzRaR0VyZEhkNWVETjZURTlpWkdWWFFWUk1TWFZSVHpWelN6VXZkVFU1S3pJMVdXTkRTVGR0TDBKRg0KTWxKQlkycHdjVXA0VGcwS1psbE5kMFp0WlVKWUsxWXpSRFJFYkU1UU15dEpXVE5xUkd4S1dsVnpRME0wVFVKMVoxSlBSVWQxUTJsMA0KYkZnMWVtcEJlR3RZY2sxb1JrUjVEUXB3YWs0eFNHSjBaR3h0T1V3dlZtMWlZbGRzU21kRksyaHhObGRVVWxCdFJIRjFlR05DYTJ4Tg0KTWxSVWJHSkNkRlVyYjNoSWJERjNRa1ZHWkhrTkNsVjBlWEpsU1ZwbE1rbG1VSGd2VjBWUWJTdFRRamt6TWtwNWNuSTBlamg0VG5SdQ0KYlZsdWQwcHRXVEZ6UlU1WVJUQkthMnRhVmtwMGFsSjJWUTBLVTJodmFWZDZaRXRPVEVVMk9GQmpOMlF2T1dOQlRHaFFNekYwTDFWeA0KU2toM1dEaHVjbUkzTTBkTFkzQnpjSE15YjBNMVdWSkVhR3RhVjNkSURRcDBSakJJUnpKMVlYRXhSa2hWS3paUVQxa3lPVmc0ZEZkQw0KWW1sclZraHpZbkk0YUhCWWFqTlBWUzlIV1VwbWNqZHRSbE01TlhKVWF6Rk5WVFFOQ2k5bGQyd3dRaTg0ZWtONldEZHBUV1ZwVVZVdg0KT1hSUU1qRlVhM1YyUVRZMFNsSTRlVEJ4UkZSaGVVbElWM1ZPY1RsSmMwaElNa3h1VEU1c1FnMEtXalptZGpGYVRrTnNURTVtUTJGVg0KYlhOQ2VHbzVORTV6TmxjNGNYSlJPR2sxVjFJd2EyWTRZME5LY25NMmEzcFBiM1ZFVkVoblJVOXJaVWx0RFFwU1IwNVlZeXRWTXpFdg0KY0V4SWRtaEtSMUZNWVZGWlowOUplRTFxWm0xWGNXTk9OV3BXWlZBeFVXeG9WbmRhYjJaeVdqVnFUbmc0UWxneFpFa05DalU0Wmt0dQ0KUVZoWmJrUlNZWE5DT1VWaVQxVk9NV3Q1WkV0alZYVmxSMk01Wkc5RVIzQmlUR3BtUVZsRk1YRlZka2RoYnpCUlRXVmFkbVZaUVEwSw0KY0d0bGRWaE9OMGsxVGtGNmNHd3pUbUkzVG5GTGIxbFBVMVEwV0VsTVZrTk1NR1ZzWVdkSVJVNUZhbXRhY0c0eFVWbDZXRXBvWnpSaQ0KYVROQ0RRcEtURVJHU2xscFJsRXdaM0ptUzNnd1pXWnRiVmRQVG5GV0swcE1TbGszU2s5eFRuVnRiV2xQVkVabWRGaFJXRlV3WWtwQw0KUVN0bWJrVXpiMDBOQ2pKVGJVRldjakp6U0NzeWJqTk1SRFpGYTA4MU1WQnpiRFpqTXpCRWJuYzVNbTVZSzAxNVZsVlhkMWhMV1VoNQ0KU0Vab1VWSXZRMng0VFZKQlZBMEtZa0ZaY2tJNFQwSndWelIwUXpSMFVtWlNkV2hsUVVoTlFYcFNhM1IzTWtOMWMzZFNlWGRMTm1nMw0KUkZCWmRFWnRaRVZEVGxoMGQxZHVWR2R1RFFweFExVk9RMWxzWVhsRFFXTlRXWEExV0hWUlNrNVhkM2N6YTNwaVptNUxXVUZ6VkRVcg0KWWpSVlJtSXpLekkyZFVaMk1rRTRTM2d3V25SWFJISU5Da2R6YmtkRmVYVTFjM3BNUlRGc1IzWTRUVFJsY1dKSFREaGtObEpxZDBzMA0KU2s1bVpYTldTa1JJVkRGNFYwNUxiR1UyYVdzeU0xSk5abXBNV1EwS05WcFBka3BUVDJjM1VGRk5RakUyUkdwMU1sVkdORGRLYms5MQ0KV1dvM2EyNW1TRmRDTm5wVVIyczVRMHBMTjBObU1sUlBUVEo2V0daT2RUTXlEUXBFUW1aMlYydEhUa1pUUTNBMmJFb3pMeXRGUWs5UA0KVlRCaWVYa3libVJ3TkZWb2RHMVlWMUZWVVcwMVpIWjFRVE5CZW1kTU1WUXdORmMzWjFrTkNtRktXVFZEVkhrMWVrRTRNbGtyTkhWcw0KWTJwWFZIbDFkamh6TXpCWlJ5OWxXVVZNWTA5aWRXTnpNVTFHTVdkQldXRlhUVE5CUjNoaFZtVklSUTBLUzJaaVUzVTBhaXMxWm5aUQ0KTVRaM1dGSnBWV0YzYUhoSFEydE9TU3MyWjBoS2FXUjFVbEIzVEM5Tk4xTmthVFI2TVVoU2QyRkdNREJ1ZFdseERRcGlhbEJDZVVRNQ0KUVVOTVdETlFiM0ZDZDBVMGQwVkliRFZDY1dsUGJVNHdTbVJ6YVhoSVRrRnFRMUIxZFVkMk5XcEJlbTVMVXpOcVF6WjRTeXNOQ2psTA0KUm1VMFRTdEVPVmhxYldkT05rdExVRU15WjAxa1RVMUlZMDU2TkVSNlUwcG9ORGxTT1ZsQ1RXNTBXblEzUXpodGFVOWplVTgzVVdZcg0KVmcwS2QwdHFVRkk1TjJoVWRVNURTWG92UVc5VlJEaHJMME55Wm5CTFVVVk1UM2R4VEhOM2NtazNSbTB4YldVd01GbGlLM0ZxUVhWVA0KV1c1amEzQjVEUW94TURSV09GVlVkaloxV1haNkt6RXZVRzF1U0Zab1FsRkdNaTkwU1c4dldqVmtaekpXZUc5bk1UQmpXR0ZEY1dGcw0KTVhWd05VRldkRzVaV0VFTkNtcENka0ZMU1hBck16UlNLeXN6YW1Wd05tRnRRMWd2VlhSNlRYaFdZVmxJVG5GNldYWm9Wa2N6UzJvNQ0KY1haYWJrOUJXamhKUjJFNFFrWnJhZzBLZHprcllWQkZka3h1Y0c1b05qaHdPV2MwTVdwWllqSkRiRTFsSzNCa1JFdFJaV1oxUlVkRA0KTkZkdVVYSklZa1JxYzNOUGNtSkVlazh2YVZKbkRRcHlNV2MwV2tGelJFd3ljMVV4UzNwMU5XOXVhazlzUXpSS1IyOW1VRGRpY1dwVg0KVW1kVFp6WnJTWFkxVVZnMFkwRk1jbG8yVm5wSk56aExNRTBOQ21GWk5DdDROMGgzYWpCbWRqQkllV0ZWVlVSWmJteFhaVmxrWkVSeg0KUlZsb1NFdFNWSEo1SzJsTk0wdHpXU3RzU25jelRsWkVRa0pKYmtkWmRRMEtVSGRCV1VKeVZFMWpXV1ZrUW13d2VTdExUREl2VURSVg0KWVM5MFExbDVabTluTjFoelRVNTNTVEpGTjI5TWNuZDFZbUkxVlZwa1dUVkpWVWd6RFFweGVEWjBiM1ZWYTJST2FsZFVUM04zY0ZoQg0KU1hod1NIUkJVV0pHTjJORVl6UnBWalpNT0ZaRVRuTndjRXR1TW5wd09WVkhjREI0UTBOWloyTU5DbmhDVnpFMVJUTkJVRGRTZG5OeA0KUVRkV1ZXYzBjWFl6Y25saVQzaDJSazEyZW5SVmFGcHNaMlEzVW0wME4wWXlSMnhpV1hkUVkyaDJTSGxHUncwS2NVMUROMDltVW1Gag0KVGpaelJWUnBkV3MzT0hKQ1dIaFZRbFkwU1ROMGNYTjNSR3hPWkU1R1kyNWlaRFpNVW1aSE5uWm9hamhsWkU5QlJDdGxEUXBCZG5wVw0KY0dSUk9VbFVWSG94ZDJZck5UUTBUa1Z6ZW1Od2REZFROMnh1TkZrdlluTlJVaXRTWlhCTGFqWkdlRWRDTkROR1dGazRXa05hTVdJTg0KQ2xwVVNuTjRSazVTVGtkMFUyZDJSeXN5UkhCSVdqaHdRMkpFVG1GSGJ5OW1abFJNUW5ZNGJWbEhSWEJGVVhCRGJYcHNZbTVLYVZseA0KYTJoUlJnMEtabFZOZUdGaUwxcHFPSGx3V0VVNFdsUkNTWGwzTkhOSlpTdFJja2N4TVRSaVIyeDNlakpKTW5oWE9HZE5PVzlFUzJsdQ0KVUVwS2NYUjVNMWgzRFFwQ2RFSTFWWFJHVTBzMk1IZEdNVWxzWjJwVE1GRldUelIwYzIxckRRbzlPRlJVTkEwS0xTMHRMUzFGVGtRZw0KVUVkUUlFMUZVMU5CUjBVdExTMHRMUTBLDQotLS0tLS1zaW5pa2FlbC0_PV8xLTE2NzEwMDM3NjcwNTcwLjc3MTM0OTYyMjUyODIyNTYtLQ0K", + "historyId": "1381250", + "internalDate": "1671003768000" + } +} \ No newline at end of file diff --git a/test/source/mock/google/google-endpoints.ts b/test/source/mock/google/google-endpoints.ts index 66ff9a9b1df..4714c2344b5 100644 --- a/test/source/mock/google/google-endpoints.ts +++ b/test/source/mock/google/google-endpoints.ts @@ -42,6 +42,12 @@ export const mockGoogleEndpoints: HandlersDefinition = { } throw new Error(`Method not implemented for ${req.url}: ${req.method}`); }, + '/oauth2/v1/tokeninfo': async ({ query: { access_token } }, req) => { + if (isGet(req)) { + return oauth.getTokenInfo(access_token); + } + throw new Error(`Method not implemented for ${req.url}: ${req.method}`); + }, '/v1/people:searchContacts': async ({ query: { query } }, req) => { if (!isGet(req)) { throw new HttpClientErr(`Method not implemented for ${req.url}: ${req.method}`); diff --git a/test/source/mock/google/live-gmail-test-emails/Message attachment with plain text(FMfcgzGrbHrBdFGBXqpFZvSkcQpKkvrM).eml b/test/source/mock/google/live-gmail-test-emails/Message attachment with plain text(FMfcgzGrbHrBdFGBXqpFZvSkcQpKkvrM).eml new file mode 100644 index 00000000000..9a100ee7309 --- /dev/null +++ b/test/source/mock/google/live-gmail-test-emails/Message attachment with plain text(FMfcgzGrbHrBdFGBXqpFZvSkcQpKkvrM).eml @@ -0,0 +1,125 @@ +Delivered-To: ci.tests.gmail@flowcrypt.dev +Received: by 2002:a05:7108:6881:0:0:0:0 with SMTP id m1csp3501320gdd; + Wed, 23 Nov 2022 23:11:50 -0800 (PST) +X-Received: by 2002:a05:6512:1698:b0:4a2:4b43:9aad with SMTP id bu24-20020a056512169800b004a24b439aadmr11542664lfb.213.1669273909922; + Wed, 23 Nov 2022 23:11:49 -0800 (PST) +ARC-Seal: i=1; a=rsa-sha256; t=1669273909; cv=none; + d=google.com; s=arc-20160816; + b=TFPMRNAu49RHwFnH9jL9V4ui+8StMCbGoibhf7Ft6OxpXFnjlaQ4qqimygdXYDJVDY + XZJkE0D8mZM6zWdXMB9GJYPjk11Av2gLaIGPIe5TR13S6MJyQ+HT/p+PTEoIWJ5Ro631 + M2AWU7X0TgwS8zahXjWFjUUeoC/7HyDwfUMbMZKm8kZkwU8KAnOT+l5rPqc3ZQh6Rjes + TGAJs+85RcqCwclIEophQlSjy/5sy150rn6ws6E5EPWMFtTYs37TbxyUEvqITOoHn9VS + rua6eUmNlCavI/YP0q1MJewnEoUA+ZjijeHRxWy2COxVpyu9dI6ir1CAsv6rlNLJquTZ + s2Vg== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; + h=to:subject:message-id:date:from:mime-version:dkim-signature; + bh=F/XlgPHNbrVFxDOCZb9zpwnSEs+8Rxm+pY2MbpmFLYY=; + b=gsFW8p+wi0hvIZSnvRYdmka17uQiNX81CndJlSc5rj3OgIj4NKEN3hPKT+uHU8JZMv + 9C3soOoGHSyl7lOGtFUAlY4uDojMMM0GnrJ/JJN5Ky5uI/hY21Z3PbIDABQM3c1b1cXI + jqX2T5wFBj2haHx1HzJCLF1f70pH8A2hQNZI5sWSr1V7ErdMoDsNg9y7sT48ReTRFuiQ + wJ5oD0JWm6ifM6tyqEhcoqZ74gj/RGQpTNxbsXXhA2T0yGS82Sz1hccIp1RVl1Vu8/uy + 6LQn+7lxbuTR2hwoLIXFZ2jfhaJVMyc+JliAtLF841hN2c9IOkZWCAZ/MC1j3nwkFEyB + 8zcg== +ARC-Authentication-Results: i=1; mx.google.com; + dkim=pass header.i=@flowcrypt.com header.s=google header.b=gx5j2Rhu; + spf=pass (google.com: domain of ioan@flowcrypt.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=ioan@flowcrypt.com; + dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=flowcrypt.com +Return-Path: +Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41]) + by mx.google.com with SMTPS id a21-20020a194f55000000b0049f53f2e88fsor64225lfk.14.2022.11.23.23.11.49 + for + (Google Transport Security); + Wed, 23 Nov 2022 23:11:49 -0800 (PST) +Received-SPF: pass (google.com: domain of ioan@flowcrypt.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; +Authentication-Results: mx.google.com; + dkim=pass header.i=@flowcrypt.com header.s=google header.b=gx5j2Rhu; + spf=pass (google.com: domain of ioan@flowcrypt.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=ioan@flowcrypt.com; + dmarc=pass (p=REJECT sp=REJECT dis=NONE) header.from=flowcrypt.com +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=flowcrypt.com; s=google; + h=to:subject:message-id:date:from:mime-version:from:to:cc:subject + :date:message-id:reply-to; + bh=F/XlgPHNbrVFxDOCZb9zpwnSEs+8Rxm+pY2MbpmFLYY=; + b=gx5j2RhucfA5vtfOIT8ay0fgGruWr7pnS36WjRn+pysCUhKNmj9qYNJeCEYL0jrugr + zuhE7WhcKPrsd12kx8GWKCfAsTY2N8WrSfmpJtNuzEqJBmT1BTK8nQglQzF1JckxDbiK + P+kT2UKkmNqPO2B/o24uv09z5Aqy8VNjYh1+8= +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; + h=to:subject:message-id:date:from:mime-version:x-gm-message-state + :from:to:cc:subject:date:message-id:reply-to; + bh=F/XlgPHNbrVFxDOCZb9zpwnSEs+8Rxm+pY2MbpmFLYY=; + b=fydWxQ0Bw3Ljs+Rx/+dhNTxMYY29ZnLaPgmwIZs9WT0LulvsTkYR005yA8j+P+3/5+ + +PzvLD6kQ41LEfRZEvcFKm09tn9fryX39776TrHQSROkdl1pyMHfHdvPhIQi/cQNI+R+ + jQnW5aEGkP+x4wtZeyZTf5YCOMu0PEsD8scJXU9Mv7bi1EiTghAbJFD1mYDgt9Ztu6Ms + 1Rp/nyWAkWZZqP7pluRDQ9oKb8CJ/swHLAs8WhcWZ2NpN5C+G8Ul/snsNQlA/LmqCaHw + l3f8C9sc1MCkpVJ0fMHhHw9L2E4sxaXebvAy06LF7uVnl6EsS9EdoYzgRiTTouMC30sk + MJEg== +X-Gm-Message-State: ANoB5pkz8Ib9pqSD/x3au0unFaDyBYh0kP+CVoe72ZzT8HYaJq7OfTOh + wMLtr2zVFIPcDjC+fuqdaFuKTWzdK59x+CCl1XYzQFQ+QNrQDw== +X-Google-Smtp-Source: AA0mqf6D5LB4JBdOMPbJkksIBdxMLpfJaqXgxBbck/qqswHVuut81WuUNCRNOZ4Sj4wbcU1K8muv0z97aNQh/DtWbQw= +X-Received: by 2002:a05:6512:2c85:b0:4a2:5937:e9b with SMTP id + dw5-20020a0565122c8500b004a259370e9bmr9997612lfb.11.1669273908939; Wed, 23 + Nov 2022 23:11:48 -0800 (PST) +MIME-Version: 1.0 +From: Ioan at FlowCrypt +Date: Thu, 24 Nov 2022 03:11:36 -0400 +Message-ID: +Subject: Message attachment with plain text +To: ci.tests.gmail@flowcrypt.dev +Content-Type: multipart/mixed; boundary="00000000000062238205ee321e74" + +--00000000000062238205ee321e74 +Content-Type: multipart/alternative; boundary="00000000000062238005ee321e72" + +--00000000000062238005ee321e72 +Content-Type: text/plain; charset="UTF-8" + +Plain message + +--00000000000062238005ee321e72 +Content-Type: text/html; charset="UTF-8" + +
    Plain message
    + +--00000000000062238005ee321e72-- +--00000000000062238205ee321e74 +Content-Type: application/octet-stream; name=message +Content-Disposition: attachment; filename=message +Content-Transfer-Encoding: base64 +Content-ID: +X-Attachment-Id: f_lauqkou20 + +LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEZsb3dDcnlwdCA1LjAuNCBHbWFp +bCBFbmNyeXB0aW9uIGZsb3djcnlwdC5jb20KQ29tbWVudDogU2VhbWxlc3NseSBzZW5kLCByZWNl +aXZlIGFuZCBzZWFyY2ggZW5jcnlwdGVkIGVtYWlsCgp3Y0ZNQStBRHYvNXY0UmdLQVEvOEN2Z2xK +dzNTeTB1TkpkMGhNcVh3RHBtb3g2bzRwZys1NW8xOEpGOXMKeTdPVFNacUVvYUtXbERLYm9mN3lk +VjhMSVF4V2gyNTBCRk8wVy9JQ1dySlNkOFErcE0zdGtZcWEyVVpZCkZ1UmpGZ0ROeUZvT2c0TGFa +OVJGbzRnSDIrR1krL0V6a1NzNGFBdmlYQzRtNVZvaEFnVnlMTEZVeWpLNgpsRS8wSWlxelJSZkMv +ME9mMGJPSmJ6QzRmKzZCMmIwSnFZTjNTOERFMEc1NVlHWDFFbjEva0hPY0lQWlgKekxLa2NBZWZO +OWtqYTQxa2JkaUJDU244V0hUK1JEalcxc2R4WCs4bTJSUi9FWDFoT25uem4xSlNGZmVyCmR2M3Fo +SElvaWxqZXh3Y0ZROEFrQldPZkRTM2R2anhCa24zSjY4blNqZGs0azA4VERCNmR6b1hiZFpkWAoy +eUMzMWo3ZDI3YW1ZL2lvM0x2Zm9TTzNIRGVUMDZmT0xkRDNXaWhTRXNrbVNSd0cvaWF6UmZCZ09S +TlEKNVJwa1I1SGs2bE5qVlp1ZnU3bXVJM2VETjNvOHpwSUdmQUh5N01VcFlaNkE3VzJRNm8yRkFx +cmxsQVpwCnNBR2g1VUJ3ZERsOThybDBJOVZwUmZndVZWR0xxYzJwV3ozUlIzaEdLZzhQRE9ZMTU1 +anJzYW01QlN6ZAp6VFg0Njd3UXoxWFc1MlZ0ellXd3IwYTVZSUtkRzRRdmtKOVc5OU5IMlQ3clk0 +NCtFNHB3eHJVQ2MzcUMKeEVjV09sYitRbGEvK3k5eUczNEtGbCtKQkZXQ1hPUHl2ZmVxeTVmNUdj +dDl5amtyUlY4YUJ3RFdDQThvClBkajJnaXFkNG5NajI0Y29NWi9mSTdWbG0rWnU2MDhxUjVVMGJE +b3lDWDdCd1V3RFMxb3YvT1l0bFFFQgpFQUNBSk52bHFKTXExNDFtbDB4TzBubEc2UitlSzhLQnA4 +emllWVhWWndpbUdXZnhqeThDV0xyamJIcjQKMWh5SkJ3TWliV0hRRVpjaEJOM1VXK3ArYjNHTFVO +aDBrOUsrYnRrOGd1dFVnTUk2aTdpOE5GaWVVcDNnCm9wZk1UNnI3UFNteGN2Yk9lRmlObmc3OExL +aWUxRTVtNmxmMC9zYWhCWUtvRmJFbGhCRFljWnZ4SGFMZApWNStNMFY4TmJLNTFMbVlIeEc5SFFF +WlNyeWJHWDljd2w2TjZLWGRFcG5JYThKWGdHT0MrdkVFS0JtNTcKQ3dWVkdPb3EvYjBwSWR5RjlG +RGM5T1hxbEVTN21BZ3M4cUROZld5M1pJbGhFak5FZlIwS2dhT2xLay82Cms1eTNLbmhhb0thSlU5 +UVY4SWhMVFprckMzNnpRdFlJdENOcDFYeU9tYm9MTnBDUEo4NjBFa3VIanhwUwo3MnF0SjFocVI0 +clk2emY3Y3FsUkt5SkNXRmNILzJyOG9oSTZQSFNKNGVWckZBUWljL2RDWHJWVkVUSE8KVnE3ZUJr +LzFTV2FLSllUVStWazF0S0RKUlpYV2ZLUDhrSEdtOG94bmd3amNPV2ZBRWlzUUFieWJ0cW5ZCkN4 +WC9nK2VFMFFlVjBNVDAzQnBNbWFTS29IOFYvYmdTTTRJMTkzTHR2UWpuS1hVb3NLMnlnRU00MjM3 +SQozd0lkUm5zRkQ5bGEzR0doZmZKTUd0YlNJZFozb0tVbmVycXpDdlN3elM1K0J3TG4xQ0QwWUN6 +bHVOTjQKb0F2Z2pHSVkyNklGaE9EOHhxREUzZFZvdUlYSVptMkluUEg0cUNvQlVEYjJ0N3dYSjVM +WER2OVd3LzI5ClNzZmdHaS9ORzVtV1BQaXZwQmJudmJNMU50SzBBWDF6Y2JTZGNkU1NJNjkvcUdC +ZkY5VWtMSHRHZTZ4ZQprVVY5aVphejR6NVpFK050V0FUd1EvaG42eVF4WDBEMWFUay9JLzNzdCtQ +U25OVjJMT3RHMEQyRGJ2WUYKMnNOZURucithSXRHWkdNNDh6dHNhdnU1YTN4TUx4ZytEUG91RlJt +aituSEhCcFFlNDYzREFIYURqM0lICmJhS1lWUWRXZllEWUJVc09lQmF6Z1hMaThnVW9PeE42azkr +RWNQeUh1cHk4S1VZRE5uaUdvY2xISGwzYwpBdW5TUXpyMlByU21KK2tyeHdOZFVjamxPZkpwCj1Z +VDJBCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0= +--00000000000062238205ee321e74-- diff --git a/test/source/mock/google/live-gmail-test-emails/encrypted text inside _message_ attachment (FMfcgzGrbHprlHvtTJscCJQpZcqrKQbg).eml b/test/source/mock/google/live-gmail-test-emails/encrypted text inside _message_ attachment (FMfcgzGrbHprlHvtTJscCJQpZcqrKQbg).eml new file mode 100644 index 00000000000..84fac312106 --- /dev/null +++ b/test/source/mock/google/live-gmail-test-emails/encrypted text inside _message_ attachment (FMfcgzGrbHprlHvtTJscCJQpZcqrKQbg).eml @@ -0,0 +1,128 @@ +Delivered-To: ci.tests.gmail@flowcrypt.dev +Received: by 2002:a05:7108:6881:0:0:0:0 with SMTP id m1csp3034396gdd; + Wed, 23 Nov 2022 04:27:21 -0800 (PST) +X-Received: by 2002:a17:902:ccce:b0:185:4880:91cd with SMTP id z14-20020a170902ccce00b00185488091cdmr8517131ple.130.1669206441269; + Wed, 23 Nov 2022 04:27:21 -0800 (PST) +ARC-Seal: i=1; a=rsa-sha256; t=1669206441; cv=none; + d=google.com; s=arc-20160816; + b=qen+yVjfdlHrw5IDnda6ePV8aGFVjpUwSNjKvqH1JLAyTg8zZTKHebwGQ55QIEiXUG + jA8FncKK/GGudfq3VeJpi+/JIfOD5WGRD8pxFHKmbaHM2eDsyrzSUBvxneog/oBgcuPk + Ej4wqEqZ2oXZBGr3n2W5RvM56sR+kt7SsLbWOdPD0ONFK31eOS4cccnBKiJDpWYeFsJV + DU3zK6xthdVv3pEIzVUgYsuJEX5o4JYrQoibMABxUy3LJLfyjeYHmXKBMcjg15y6w7Xo + J/0KjR/vUkIIIZjcY4/xRrNB0WlKSqhDRFOsU4WC8SXqosulurE63L0d+wb28FzkUXoW + auOQ== +ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; + h=to:subject:message-id:date:from:mime-version:dkim-signature; + bh=HchlU3hA9uE4MpkYn4TDhnb9Efjs0AinaDOLnDza86E=; + b=NCcF+VCtBBPK+oFZG75li6y9352bTs3Jg+jPQ5tsbTwuP0slsjjS6NB3+kcwnpIwpM + wNM5LaWckkIflKWtr9NyHhbSNRcnxxvIbk5wMwoJhNxzcA590qgWewURWV+JajKy9Te0 + P3jr/xBTZqWEVbrK5zKUAsL9/supkVAsv9ELzNAcqsqzNKt3csoV2g4HJyuuab2zNWrS + tcVAKdwQYh73+GUvrdX7Z1j2bFhT+2dO7M2rtyfwZujH7R4j9CkMBOCMMyBP6Lhhw1sw + 1p5DhQtL4v4s34ZebEnopwL7j0mU6cZKu13DD7AzWvXs3kyvDnsCiZ7C4M1ltr+UhR0x + /14w== +ARC-Authentication-Results: i=1; mx.google.com; + dkim=pass header.i=@gmail.com header.s=20210112 header.b=GnmfGvpc; + spf=pass (google.com: domain of flowcrypt.compatibility@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=flowcrypt.compatibility@gmail.com; + dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com +Return-Path: +Received: from mail-sor-f41.google.com (mail-sor-f41.google.com. [209.85.220.41]) + by mx.google.com with SMTPS id h7-20020a62b407000000b0056075691fd3sor9141994pfn.0.2022.11.23.04.27.21 + for + (Google Transport Security); + Wed, 23 Nov 2022 04:27:21 -0800 (PST) +Received-SPF: pass (google.com: domain of flowcrypt.compatibility@gmail.com designates 209.85.220.41 as permitted sender) client-ip=209.85.220.41; +Authentication-Results: mx.google.com; + dkim=pass header.i=@gmail.com header.s=20210112 header.b=GnmfGvpc; + spf=pass (google.com: domain of flowcrypt.compatibility@gmail.com designates 209.85.220.41 as permitted sender) smtp.mailfrom=flowcrypt.compatibility@gmail.com; + dmarc=pass (p=NONE sp=QUARANTINE dis=NONE) header.from=gmail.com +DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=gmail.com; s=20210112; + h=to:subject:message-id:date:from:mime-version:from:to:cc:subject + :date:message-id:reply-to; + bh=HchlU3hA9uE4MpkYn4TDhnb9Efjs0AinaDOLnDza86E=; + b=GnmfGvpcmvgRf191dPoWTRIoGBJzZBTGtvf2yqI6/LKafir8r8klkfsY8o67TU6hmE + 2ryB+WPKZutgu1lcWqI10cAVsHqqdSt/u4z0PtBilcn1KPFlvuHbQizAGcIdnXPOZRAY + 8CjOtFj8z+EFanyw6DAibRxUsrrFQMK2UFi74UMnw6830EpJWgVZVWxTv1PCxA8ie4od + Lo6JXJcVhtlvMnn6UxAcUQtgf5YmjUTWYAudIEkNA8PA8LB8MDUFhs423l6T+XcKUe4B + H8+NFRbCerO7xUyTYwnXy/xp5Ct4hshUOvMjXenREE6s+7z2wTLNEYASc57olhWjRSh7 + M9OQ== +X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; + d=1e100.net; s=20210112; + h=to:subject:message-id:date:from:mime-version:x-gm-message-state + :from:to:cc:subject:date:message-id:reply-to; + bh=HchlU3hA9uE4MpkYn4TDhnb9Efjs0AinaDOLnDza86E=; + b=04a50tzfpQ6h8BVV2Tp36MZHPLC3omtLQfU/WdWknUEMcgKguxGHv+x+U5Ba3f8dKH + khNQ2u52CXk6Z4lHgqM2Yi/unZSzn02/JzMt+nCkueS3cz+cyDazKlujOH/iDuhUZla5 + HNGk508OJMxjWUGwg0alyWy6tsTjWosqmshFOOHmqEcNJRkFrha0fNUhTyPjxpvh9ilx + vAxcBNHB1N+84/jvQNvj2SKcn/m5GixVOS6GcVQTbYOZXpwRq91XEyaVY8/Ml8+UgICP + INU6rf9NmJa1O1IDAxS4hmKbYdY2eeIEc4Zuvpt0EayN2aaIzYlQrbc8Pae0aazKR1EP + PRGQ== +X-Gm-Message-State: ANoB5plwy6XI7jdoh8thHiyNuVIABUkhrlVg4ydHiwoNb3mi0xZwX8fd + uRMtem74UVXlNQRE6v26o2mre1Ezy/UuEcD1bgnAvt6lSxx0Zg== +X-Google-Smtp-Source: AA0mqf6WM5lk9p1ozN+vYmFtRfYbVtNONB0Pl6FZKF6ZrubJEC99XeRcCHVgPw3YjI5SkHZhQq7RJk5umENE8AtjrI4= +X-Received: by 2002:a05:6a00:2396:b0:572:698b:5fa9 with SMTP id + f22-20020a056a00239600b00572698b5fa9mr8787722pfc.28.1669206440459; Wed, 23 + Nov 2022 04:27:20 -0800 (PST) +MIME-Version: 1.0 +From: FlowCrypt Compatibility +Date: Wed, 23 Nov 2022 08:27:08 -0400 +Message-ID: +Subject: encrypted text inside "message" attachment +To: ci.tests.gmail@flowcrypt.dev +Content-Type: multipart/mixed; boundary="000000000000f15e0805ee226866" + +--000000000000f15e0805ee226866 +Content-Type: multipart/alternative; boundary="000000000000f15e0705ee226864" + +--000000000000f15e0705ee226864 +Content-Type: text/plain; charset="UTF-8" + + + +--000000000000f15e0705ee226864 +Content-Type: text/html; charset="UTF-8" + +

    + +--000000000000f15e0705ee226864-- +--000000000000f15e0805ee226866 +Content-Type: application/octet-stream; name=message +Content-Disposition: attachment; filename=message +Content-Transfer-Encoding: base64 +Content-ID: +X-Attachment-Id: f_latmek3c0 + +LS0tLS1CRUdJTiBQR1AgTUVTU0FHRS0tLS0tClZlcnNpb246IEZsb3dDcnlwdCA1LjAuNCBHbWFp +bCBFbmNyeXB0aW9uIGZsb3djcnlwdC5jb20KQ29tbWVudDogU2VhbWxlc3NseSBzZW5kLCByZWNl +aXZlIGFuZCBzZWFyY2ggZW5jcnlwdGVkIGVtYWlsCgp3Y0ZNQStBRHYvNXY0UmdLQVEvOEN2Z2xK +dzNTeTB1TkpkMGhNcVh3RHBtb3g2bzRwZys1NW8xOEpGOXMKeTdPVFNacUVvYUtXbERLYm9mN3lk +VjhMSVF4V2gyNTBCRk8wVy9JQ1dySlNkOFErcE0zdGtZcWEyVVpZCkZ1UmpGZ0ROeUZvT2c0TGFa +OVJGbzRnSDIrR1krL0V6a1NzNGFBdmlYQzRtNVZvaEFnVnlMTEZVeWpLNgpsRS8wSWlxelJSZkMv +ME9mMGJPSmJ6QzRmKzZCMmIwSnFZTjNTOERFMEc1NVlHWDFFbjEva0hPY0lQWlgKekxLa2NBZWZO +OWtqYTQxa2JkaUJDU244V0hUK1JEalcxc2R4WCs4bTJSUi9FWDFoT25uem4xSlNGZmVyCmR2M3Fo +SElvaWxqZXh3Y0ZROEFrQldPZkRTM2R2anhCa24zSjY4blNqZGs0azA4VERCNmR6b1hiZFpkWAoy +eUMzMWo3ZDI3YW1ZL2lvM0x2Zm9TTzNIRGVUMDZmT0xkRDNXaWhTRXNrbVNSd0cvaWF6UmZCZ09S +TlEKNVJwa1I1SGs2bE5qVlp1ZnU3bXVJM2VETjNvOHpwSUdmQUh5N01VcFlaNkE3VzJRNm8yRkFx +cmxsQVpwCnNBR2g1VUJ3ZERsOThybDBJOVZwUmZndVZWR0xxYzJwV3ozUlIzaEdLZzhQRE9ZMTU1 +anJzYW01QlN6ZAp6VFg0Njd3UXoxWFc1MlZ0ellXd3IwYTVZSUtkRzRRdmtKOVc5OU5IMlQ3clk0 +NCtFNHB3eHJVQ2MzcUMKeEVjV09sYitRbGEvK3k5eUczNEtGbCtKQkZXQ1hPUHl2ZmVxeTVmNUdj +dDl5amtyUlY4YUJ3RFdDQThvClBkajJnaXFkNG5NajI0Y29NWi9mSTdWbG0rWnU2MDhxUjVVMGJE +b3lDWDdCd1V3RFMxb3YvT1l0bFFFQgpFQUNBSk52bHFKTXExNDFtbDB4TzBubEc2UitlSzhLQnA4 +emllWVhWWndpbUdXZnhqeThDV0xyamJIcjQKMWh5SkJ3TWliV0hRRVpjaEJOM1VXK3ArYjNHTFVO +aDBrOUsrYnRrOGd1dFVnTUk2aTdpOE5GaWVVcDNnCm9wZk1UNnI3UFNteGN2Yk9lRmlObmc3OExL +aWUxRTVtNmxmMC9zYWhCWUtvRmJFbGhCRFljWnZ4SGFMZApWNStNMFY4TmJLNTFMbVlIeEc5SFFF +WlNyeWJHWDljd2w2TjZLWGRFcG5JYThKWGdHT0MrdkVFS0JtNTcKQ3dWVkdPb3EvYjBwSWR5RjlG +RGM5T1hxbEVTN21BZ3M4cUROZld5M1pJbGhFak5FZlIwS2dhT2xLay82Cms1eTNLbmhhb0thSlU5 +UVY4SWhMVFprckMzNnpRdFlJdENOcDFYeU9tYm9MTnBDUEo4NjBFa3VIanhwUwo3MnF0SjFocVI0 +clk2emY3Y3FsUkt5SkNXRmNILzJyOG9oSTZQSFNKNGVWckZBUWljL2RDWHJWVkVUSE8KVnE3ZUJr +LzFTV2FLSllUVStWazF0S0RKUlpYV2ZLUDhrSEdtOG94bmd3amNPV2ZBRWlzUUFieWJ0cW5ZCkN4 +WC9nK2VFMFFlVjBNVDAzQnBNbWFTS29IOFYvYmdTTTRJMTkzTHR2UWpuS1hVb3NLMnlnRU00MjM3 +SQozd0lkUm5zRkQ5bGEzR0doZmZKTUd0YlNJZFozb0tVbmVycXpDdlN3elM1K0J3TG4xQ0QwWUN6 +bHVOTjQKb0F2Z2pHSVkyNklGaE9EOHhxREUzZFZvdUlYSVptMkluUEg0cUNvQlVEYjJ0N3dYSjVM +WER2OVd3LzI5ClNzZmdHaS9ORzVtV1BQaXZwQmJudmJNMU50SzBBWDF6Y2JTZGNkU1NJNjkvcUdC +ZkY5VWtMSHRHZTZ4ZQprVVY5aVphejR6NVpFK050V0FUd1EvaG42eVF4WDBEMWFUay9JLzNzdCtQ +U25OVjJMT3RHMEQyRGJ2WUYKMnNOZURucithSXRHWkdNNDh6dHNhdnU1YTN4TUx4ZytEUG91RlJt +aituSEhCcFFlNDYzREFIYURqM0lICmJhS1lWUWRXZllEWUJVc09lQmF6Z1hMaThnVW9PeE42azkr +RWNQeUh1cHk4S1VZRE5uaUdvY2xISGwzYwpBdW5TUXpyMlByU21KK2tyeHdOZFVjamxPZkpwCj1Z +VDJBCi0tLS0tRU5EIFBHUCBNRVNTQUdFLS0tLS0= +--000000000000f15e0805ee226866-- diff --git a/test/source/mock/google/strategies/send-message-strategy.ts b/test/source/mock/google/strategies/send-message-strategy.ts index 347343b791e..04e546432ad 100644 --- a/test/source/mock/google/strategies/send-message-strategy.ts +++ b/test/source/mock/google/strategies/send-message-strategy.ts @@ -72,15 +72,15 @@ class PwdEncryptedMessageWithFlowCryptComApiTestStrategy implements ITestMsgStra class PwdEncryptedMessageWithFesIdTokenTestStrategy implements ITestMsgStrategy { public test = async (parseResult: ParseMsgResult, id: string) => { const mimeMsg = parseResult.mimeMsg; - const expectedSenderEmail = 'user@standardsubdomainfes.test:8001'; + const expectedSenderEmail = 'user@standardsubdomainfes.localhost:8001'; expect(mimeMsg.from!.text).to.equal(`First Last <${expectedSenderEmail}>`); - if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.test:8001/message/FES-MOCK-MESSAGE-FOR-TO@EXAMPLE.COM-ID')) { + if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.localhost:8001/message/FES-MOCK-MESSAGE-FOR-TO@EXAMPLE.COM-ID')) { expect((mimeMsg.to as AddressObject).text).to.equal('Mr To '); // tslint:disable-next-line:no-unused-expression expect(mimeMsg.cc).to.be.an.undefined; // eslint-disable-line no-unused-expressions // tslint:disable-next-line:no-unused-expression expect(mimeMsg.bcc).to.be.an.undefined; // eslint-disable-line no-unused-expressions - } else if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.test:8001/message/FES-MOCK-MESSAGE-FOR-BCC@EXAMPLE.COM-ID')) { + } else if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.localhost:8001/message/FES-MOCK-MESSAGE-FOR-BCC@EXAMPLE.COM-ID')) { expect((mimeMsg.to as AddressObject).text).to.equal('Mr Bcc '); // tslint:disable-next-line:no-unused-expression expect(mimeMsg.cc).to.be.an.undefined; // eslint-disable-line no-unused-expressions @@ -99,9 +99,9 @@ class PwdEncryptedMessageWithFesIdTokenTestStrategy implements ITestMsgStrategy class PwdEncryptedMessageWithFesPubkeyRecipientInBccTestStrategy implements ITestMsgStrategy { public test = async (parseResult: ParseMsgResult, id: string) => { const mimeMsg = parseResult.mimeMsg; - const expectedSenderEmail = 'user3@standardsubdomainfes.test:8001'; + const expectedSenderEmail = 'user3@standardsubdomainfes.localhost:8001'; expect(mimeMsg.from!.text).to.equal(`First Last <${expectedSenderEmail}>`); - if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.test:8001/message/FES-MOCK-MESSAGE-FOR-TO@EXAMPLE.COM-ID')) { + if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.localhost:8001/message/FES-MOCK-MESSAGE-FOR-TO@EXAMPLE.COM-ID')) { expect(mimeMsg.text!).to.include(`${expectedSenderEmail} has sent you a password-encrypted email`); expect(mimeMsg.text!).to.include('Follow this link to open it'); expect((mimeMsg.to as AddressObject).text).to.equal('to@example.com'); @@ -126,7 +126,7 @@ class PwdEncryptedMessageWithFesPubkeyRecipientInBccTestStrategy implements ITes expect(mimeMsg.cc).to.be.an.undefined; // eslint-disable-line no-unused-expressions // tslint:disable-next-line:no-unused-expression expect(mimeMsg.to).to.be.an.undefined; // eslint-disable-line no-unused-expressions - expect((mimeMsg.headers.get('reply-to') as AddressObject).text).to.equal('First Last , to@example.com'); + expect((mimeMsg.headers.get('reply-to') as AddressObject).text).to.equal('First Last , to@example.com'); } await new SaveMessageInStorageStrategy().test(parseResult, id); }; @@ -135,7 +135,7 @@ class PwdEncryptedMessageWithFesPubkeyRecipientInBccTestStrategy implements ITes class PwdEncryptedMessageWithFesReplyBadRequestTestStrategy implements ITestMsgStrategy { public test = async (parseResult: ParseMsgResult, id: string) => { const mimeMsg = parseResult.mimeMsg; - const expectedSenderEmail = 'user4@standardsubdomainfes.test:8001'; + const expectedSenderEmail = 'user4@standardsubdomainfes.localhost:8001'; expect(mimeMsg.from!.text).to.equal(`First Last <${expectedSenderEmail}>`); const to = parsedMailAddressObjectAsArray(mimeMsg.to).concat(parsedMailAddressObjectAsArray(mimeMsg.cc)).concat(parsedMailAddressObjectAsArray(mimeMsg.bcc)); expect(to.length).to.equal(1); @@ -157,9 +157,9 @@ class PwdEncryptedMessageWithFesReplyBadRequestTestStrategy implements ITestMsgS class PwdEncryptedMessageWithFesReplyRenderingTestStrategy implements ITestMsgStrategy { public test = async (parseResult: ParseMsgResult, id: string) => { const mimeMsg = parseResult.mimeMsg; - const expectedSenderEmail = 'user2@standardsubdomainfes.test:8001'; + const expectedSenderEmail = 'user2@standardsubdomainfes.localhost:8001'; expect(mimeMsg.from!.text).to.equal(`First Last <${expectedSenderEmail}>`); - if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.test:8001/message/FES-MOCK-MESSAGE-FOR-SENDER@DOMAIN.COM-ID')) { + if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.localhost:8001/message/FES-MOCK-MESSAGE-FOR-SENDER@DOMAIN.COM-ID')) { expect(mimeMsg.text!).to.include(`${expectedSenderEmail} has sent you a password-encrypted email`); expect(mimeMsg.text!).to.include('Follow this link to open it'); expect((mimeMsg.to as AddressObject).text).to.equal('sender@domain.com'); @@ -169,7 +169,7 @@ class PwdEncryptedMessageWithFesReplyRenderingTestStrategy implements ITestMsgSt expect(mimeMsg.bcc).to.be.an.undefined; // eslint-disable-line no-unused-expressions // tslint:disable-next-line:no-unused-expression expect(mimeMsg.headers.get('reply-to')).to.be.an.undefined; // eslint-disable-line no-unused-expressions - } else if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.test:8001/message/FES-MOCK-MESSAGE-FOR-TO@EXAMPLE.COM-ID')) { + } else if (mimeMsg.text?.includes('http://fes.standardsubdomainfes.localhost:8001/message/FES-MOCK-MESSAGE-FOR-TO@EXAMPLE.COM-ID')) { expect(mimeMsg.text!).to.include(`${expectedSenderEmail} has sent you a password-encrypted email`); expect(mimeMsg.text!).to.include('Follow this link to open it'); expect((mimeMsg.to as AddressObject).text).to.equal('to@example.com'); @@ -194,7 +194,7 @@ class PwdEncryptedMessageWithFesReplyRenderingTestStrategy implements ITestMsgSt expect(mimeMsg.cc).to.be.an.undefined; // eslint-disable-line no-unused-expressions // tslint:disable-next-line:no-unused-expression expect(mimeMsg.bcc).to.be.an.undefined; // eslint-disable-line no-unused-expressions - expect((mimeMsg.headers.get('reply-to') as AddressObject).text).to.equal('First Last , sender@domain.com, to@example.com'); + expect((mimeMsg.headers.get('reply-to') as AddressObject).text).to.equal('First Last , sender@domain.com, to@example.com'); } await new SaveMessageInStorageStrategy().test(parseResult, id); }; @@ -388,6 +388,8 @@ export class TestBySubjectStrategyContext { this.strategy = new SaveMessageInStorageStrategy(); } else if (subject.includes('Message With Test Text')) { this.strategy = new SaveMessageInStorageStrategy(); + } else if (subject.includes('PWD encrypted message after reconnect account')) { + this.strategy = new SaveMessageInStorageStrategy(); } else if (subject.includes('send with single S/MIME cert')) { this.strategy = new SmimeEncryptedMessageStrategy(); } else if (subject.includes('send with several S/MIME certs')) { diff --git a/test/source/mock/key-manager/key-manager-endpoints.ts b/test/source/mock/key-manager/key-manager-endpoints.ts index d479b22df2b..0c3441f9387 100644 --- a/test/source/mock/key-manager/key-manager-endpoints.ts +++ b/test/source/mock/key-manager/key-manager-endpoints.ts @@ -204,22 +204,39 @@ yeSm0uVPwODhwX7ezB9jW6uVt0R8S8iM3rQdEMsA/jDep5LNn47K6o8VrDt0zYo6 -----END PGP PRIVATE KEY BLOCK----- `; +interface MockKMKeyRes { + [acct: string]: { + response?: { + privateKeys: { + decryptedPrivateKey: string + }[] + }, + badRequestError?: string + } +} + export const MOCK_KM_LAST_INSERTED_KEY: { [acct: string]: { privateKey: string } } = {}; // accessed from test runners -export const MOCK_KM_UPDATING_KEY: { [acct: string]: { response?: { privateKeys: { decryptedPrivateKey: string }[] }, badRequestError?: string } } = {}; +export const MOCK_KM_KEYS: MockKMKeyRes = {}; export const mockKeyManagerEndpoints: HandlersDefinition = { '/flowcrypt-email-key-manager/v1/keys/private': async ({ body }, req) => { const acctEmail = oauth.checkAuthorizationHeaderWithIdToken(req.headers.authorization); if (isGet(req)) { - if (acctEmail === 'wkd@google.mock.flowcryptlocal.test:8001') { + if (acctEmail === 'wkd@google.mock.localhost:8001') { return { privateKeys: [{ decryptedPrivateKey: testConstants.wkdAtgooglemockflowcryptlocalcom8001Private }] }; } if (acctEmail === 'get.key@key-manager-autogen.flowcrypt.test') { return { privateKeys: [{ decryptedPrivateKey: testConstants.existingPrv }] }; } + if (acctEmail === 'user@key-manager-disabled-password-message.flowcrypt.test') { + return { privateKeys: [{ decryptedPrivateKey: testConstants.existingPrv }] }; + } + if (acctEmail === 'user@custom-sks.flowcrypt.test') { + return { privateKeys: [{ decryptedPrivateKey: testConstants.existingPrv }] }; + } if (acctEmail.includes('updating.key')) { - const { response, badRequestError } = MOCK_KM_UPDATING_KEY[acctEmail]; + const { response, badRequestError } = MOCK_KM_KEYS[acctEmail]; if (response !== undefined && badRequestError === undefined) { return response; } @@ -267,9 +284,15 @@ export const mockKeyManagerEndpoints: HandlersDefinition = { if (acctEmail === 'get.error@key-manager-autogen.flowcrypt.test') { throw new Error('Intentional error for get.error to test client behavior'); } + if (acctEmail === 'settings@key-manager-autogen.flowcrypt.test') { + return { privateKeys: [{ decryptedPrivateKey: testConstants.existingPrv }] }; + } if (acctEmail === 'settings@settings.flowcrypt.test') { return { privateKeys: [{ decryptedPrivateKey: testConstants.existingPrv }] }; } + if (acctEmail === 'test-update@settings.flowcrypt.test') { + return { privateKeys: [{ decryptedPrivateKey: testConstants.existingPrv }] }; + } throw new HttpClientErr(`Unexpectedly calling mockKeyManagerEndpoints:/v1/keys/private GET with acct ${acctEmail}`); } if (isPut(req)) { diff --git a/test/source/mock/lib/oauth.ts b/test/source/mock/lib/oauth.ts index 184c4938f3a..a05a53f63c3 100644 --- a/test/source/mock/lib/oauth.ts +++ b/test/source/mock/lib/oauth.ts @@ -15,6 +15,7 @@ export class OauthMock { public redirectUri = 'https://google.localhost:8001/robots.txt'; private authCodesByAcct: { [acct: string]: string } = {}; + private scopesByAccessToken: { [token: string]: string } = {}; private refreshTokenByAuthCode: { [authCode: string]: string } = {}; private accessTokenByRefreshToken: { [refreshToken: string]: string } = {}; private acctByAccessToken: { [acct: string]: string } = {}; @@ -26,13 +27,14 @@ export class OauthMock { }; public successResult = (acct: string, state: string, scope: string) => { - const authCode = `mock-auth-code-${acct.replace(/[^a-z0-9]+/g, '')}`; - const refreshToken = `mock-refresh-token-${acct.replace(/[^a-z0-9]+/g, '')}`; - const accessToken = `mock-access-token-${acct.replace(/[^a-z0-9]+/g, '')}`; + const authCode = `mock-auth-code-${Str.sloppyRandom(4)}-${acct.replace(/[^a-z0-9]+/g, '')}`; + const refreshToken = `mock-refresh-token-${Str.sloppyRandom(4)}-${acct.replace(/[^a-z0-9]+/g, '')}`; + const accessToken = `mock-access-token-${Str.sloppyRandom(4)}-${acct.replace(/[^a-z0-9]+/g, '')}`; this.authCodesByAcct[acct] = authCode; this.refreshTokenByAuthCode[authCode] = refreshToken; this.accessTokenByRefreshToken[refreshToken] = accessToken; this.acctByAccessToken[accessToken] = acct; + this.scopesByAccessToken[accessToken] = `${(this.scopesByAccessToken[accessToken] ?? '')} ${scope}`; const url = new URL(this.redirectUri); url.searchParams.set('code', authCode); url.searchParams.set('scope', scope); @@ -49,6 +51,10 @@ export class OauthMock { return { access_token, refresh_token, expires_in: this.expiresIn, id_token, token_type: 'refresh_token' }; // guessed the token_type }; + public getTokenInfo = (token: string) => { + return { scope: this.scopesByAccessToken[token], email: this.acctByAccessToken[token], expires_in: 3600, token_type: "Bearer" }; + }; + public getTokenResponse = (refreshToken: string) => { try { const access_token = this.getAccessToken(refreshToken); diff --git a/test/source/mock/sks/sks-endpoints.ts b/test/source/mock/sks/sks-endpoints.ts index 820c16109d7..e55f9d7133e 100644 --- a/test/source/mock/sks/sks-endpoints.ts +++ b/test/source/mock/sks/sks-endpoints.ts @@ -3,6 +3,38 @@ import { HandlersDefinition } from '../all-apis-mock'; import { HttpClientErr } from '../lib/api'; +const testSksKey = `-----BEGIN PGP PUBLIC KEY BLOCK----- + +mQENBGORiLoBCACf/uwJSX00H6sIrquo3bIrPnn0svWhF9NKQnS3b6DljP2tw6w7 +oHuEMhBXObJVD18rcucIcBHIeMdGybHGtKftUMqnMsEbI7HS0Chp8PSJwalGWkUY +7djEWrg7guGFTVtgkC+f/VYGjr9WZ/Z4qvhj74jo3S42xZqhK2Uo55cc9K1FeW9o +r+Q+WGUNY29vr6m8cKNkB8A+vFbai1bgq3AHZuQ4NxOsC5oU/d3yA5uFDIQVlwit +oRedKpSW5aUr6fb+pnMQzMWPVJ3xURC4QU8ZKNqZfL4TSw0TIln12Y5rAzFct9fI +cwvVFDNm7cj6ivV96mA7/BZu7dfokYv1o8C1ABEBAAG0LlRlc3QgU0tTIHVzZXIg +PHRlc3RAY3VzdG9tLXNrcy5mbG93Y3J5cHQudGVzdD6JAU4EEwEIADgWIQS3uYUU +G5E+/xcfykDnSIDoh98vAgUCY5GIugIbAwULCQgHAgYVCgkICwIEFgIDAQIeAQIX +gAAKCRDnSIDoh98vAlpYB/0YZTUwq4gOjdGIBy3hy7bf4YC1Djg8uXG/fMrXTqzN +uomi4OW7TBU/ud/TPN9Pm2Ac1OhPotft8z6sVrei94ZDRE++w8XwnPN4+wez1nAz +Ho+4mbJ08/Vgrw+I5lysCXJosFJdnex6WNqxSwhutiTE6scn/JZSVyTP6z4e3/kI +gzi5OMZlZt5OOijjw2XSE49mjfGkU9vLpJtFHNmurZ1md8c8YBV+2eA85PTZHw8v +wJpWOfHF3TjVNKlrpvPAfqVsvUqFrvEd1gm/SS/0aWmjLXPlSUXbB4yUhqLzatnX +XQVNZJ+X6n2/wHqiIypAY+VccNcNqzWdpz9i8ENmQV8+uQENBGORiLoBCADQrtb5 +5qOGz6SojFjQIYaKNhuYt7vOFWeTrkQz+0WGDku7VuWQ+kThM419cmO41ut6VOxt +yfVLmyT10hQVy44gB4GIHv2z+MNDRa4HO6gk/6i4jd1sKsbtaB1RQtwU/e0PA/aD +VPdWsAbxJM7V2/m8OFv0WIqoGfcyuC6T8bM6bNisXzVLtR003i2vgavz/8UzAWWp +57Vcmy9iTTfExIrMFjCieZpIGeAjcPl6Vod4My0TzMcBy/Vjqx+oJ2mm001F21hW +oEDPhk39h8OHf6oDh5iQ1RdtmrucCfD9z9Ub3mGhLjd4jXXPlWG1XeG6aSwfyQhk +3/HtTw8Uj5pl+Cy1ABEBAAGJATYEGAEIACAWIQS3uYUUG5E+/xcfykDnSIDoh98v +AgUCY5GIugIbDAAKCRDnSIDoh98vAjEWB/9kQIUfv8COF/8lIBYdIrT926WkmiT7 +8vrwltWXjj29QxFzpGdUpcq0rYnlpbUWY1v8fEbRk8yLJ3Ov3LN7V2gd/jKIJ3Dq +Q8OZp8W+rG/dPNEDgJ1DtBEJ0P+FB4wOLrq8ln3A3YL9HEIkdBGI7FFc1yyDl/Mk +dSXOtf/5FWSvXjM8KzhLypvvPlIzgcwCI/zApIpuBp7PplOgEkMJP07j+596D3aL +TX/4oIoeRrXu4csAJhSXUnDzkYi4L0SUALWyCa6xhmeCelrNXRx6d1jWoB+1imDQ +pEEgWM8skofFtmXLcWu29li9yf1V6w7C+A8Dp8cMStJ0OWglF2J0Luas +=PhzD +-----END PGP PUBLIC KEY BLOCK----- +`; + const johnDoeExampleCom = `-----BEGIN PGP PUBLIC KEY BLOCK----- xsFNBF9fF2MBEADD688NUzgftfhtKyVmjdbFt6oUgOYm2EpivkqhnufeB0En09hi @@ -102,4 +134,10 @@ export const mockSksEndpoints: HandlersDefinition = { '/pks/lookup?search=nobody%40example.com&fingerprint=on&exact=on&options=mr&op=index': async () => { // by email throw new HttpClientErr('Pubkey not found', 404); }, + '/pks/lookup?search=test%40custom-sks.flowcrypt.test&fingerprint=on&exact=on&options=mr&op=index': async () => { // by email + return `info:1:10\npub:57631589DB543FB10B765C2F5F0CEF862479A17C:1:2048:1600067427::\nuid:Test :1600067427::`; + }, + '/pks/lookup?op=get&search=0x5F0CEF862479A17C&options=mr': async () => { // by fp + return testSksKey; // for test@custom-sks.flowcrypt.test + }, }; diff --git a/test/source/mock/wkd/wkd-endpoints.ts b/test/source/mock/wkd/wkd-endpoints.ts index 468b50a47e0..96617db678d 100644 --- a/test/source/mock/wkd/wkd-endpoints.ts +++ b/test/source/mock/wkd/wkd-endpoints.ts @@ -141,7 +141,7 @@ CAD/VjKYjwJ4MYpcKZ7G3qYvrb3l7m2NTJLAi1yVTm1e5wU= export const mockWkdEndpoints: HandlersDefinition = { '/.well-known/openpgpkey/hu/st5or5guodbnsiqbzp6i34xw59h1sgmw?l=wkd': async () => { - // direct for wkd@google.mock.flowcryptlocal.test:8001 + // direct for wkd@google.mock.localhost:8001 const pub = await KeyUtil.asPublicKey(await KeyUtil.parse(testConstants.wkdAtgooglemockflowcryptlocalcom8001Private)); return Buffer.from((await PgpArmor.dearmor(KeyUtil.armor(pub))).data); }, diff --git a/test/source/test.ts b/test/source/test.ts index a4bb0374c4f..3d2f77a6175 100644 --- a/test/source/test.ts +++ b/test/source/test.ts @@ -32,8 +32,8 @@ process.setMaxListeners(60); const consts = { // higher concurrency can cause 429 google errs when composing TIMEOUT_SHORT: minutes(1), TIMEOUT_EACH_RETRY: minutes(3), - TIMEOUT_ALL_RETRIES: minutes(18), // this has to suffer waiting for semaphore between retries, thus almost the same as below - TIMEOUT_OVERALL: minutes(19), + TIMEOUT_ALL_RETRIES: minutes(25), // this has to suffer waiting for semaphore between retries, thus almost the same as below + TIMEOUT_OVERALL: minutes(20), ATTEMPTS: testGroup === 'STANDARD-GROUP' ? oneIfNotPooled(3) : process.argv.includes('--retry=false') ? 1 : 3, POOL_SIZE: oneIfNotPooled(isMock ? 20 : 3), PROMISE_TIMEOUT_OVERALL: undefined as unknown as Promise, // will be set right below @@ -127,7 +127,7 @@ ava.default.after.always('evaluate Catch.reportErr errors', async t => { 'BrowserMsg(processAndStoreKeysFromEkmLocally) sendRawResponse::Error: Some keys could not be parsed', 'BrowserMsg(ajax) Bad Request: 400 when GET-ing https://localhost:8001/flowcrypt-email-key-manager/v1/keys/private (no body): -> RequestTimeout' ].includes(e.message)) - // below for test "user4@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal - a send fails with gateway update error" + // below for test "user4@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - a send fails with gateway update error" .filter(e => !e.message.includes('Test error')) // below for test "no.fes@example.com - skip FES on consumer, show friendly message on enterprise" .filter(e => !e.trace.includes('-1 when GET-ing https://fes.example.com')) diff --git a/test/source/tests/compose.ts b/test/source/tests/compose.ts index 9ee478199ec..4730c9bd411 100644 --- a/test/source/tests/compose.ts +++ b/test/source/tests/compose.ts @@ -124,6 +124,20 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te }); })); + ava.default('user@key-manager-disabled-password-message.flowcrypt.test - disabled flowcrypt hosted password protected messages', testWithBrowser(undefined, async (t, browser) => { + const acct = 'user@key-manager-disabled-password-message.flowcrypt.test'; + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); + await SetupPageRecipe.autoSetupWithEKM(settingsPage); + const composePage = await ComposePageRecipe.openStandalone(t, browser, acct); + await ComposePageRecipe.fillMsg(composePage, { to: 'test@gmail.com' }, 'should disable flowcrypt hosted password protected message'); + await composePage.notPresent('@password-input-container'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await PageRecipe.waitForModalAndRespond(composePage, 'error', { + contentToCheck: `Some recipients don't have encryption set up. Please import their public keys or ask them to install Flowcrypt.`, + clickOn: 'confirm' + }); + })); + ava.default('compose - signed with entered pass phrase + will remember pass phrase in session', testWithBrowser('ci.tests.gmail', async (t, browser) => { const k = Config.key('ci.tests.gmail'); const settingsPage = await browser.newPage(t, TestUrls.extensionSettings('ci.tests.gmail@flowcrypt.test')); @@ -228,7 +242,7 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te })); ava.default(`compose - auto include pubkey is inactive when our key is available on Wkd`, testWithBrowser(undefined, async (t, browser) => { - const acct = 'wkd@google.mock.flowcryptlocal.test:8001'; + const acct = 'wkd@google.mock.localhost:8001'; const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.autoSetupWithEKM(settingsPage); const composePage = await ComposePageRecipe.openStandalone(t, browser, acct); @@ -1752,13 +1766,8 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te }); })); - /** - * You need the following lines in /etc/hosts: - * 127.0.0.1 standardsubdomainfes.test - * 127.0.0.1 fes.standardsubdomainfes.test - */ - ava.default('user@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal', testWithBrowser(undefined, async (t, browser) => { - const acct = 'user@standardsubdomainfes.test:8001'; // added port to trick extension into calling the mock + ava.default('user@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal', testWithBrowser(undefined, async (t, browser) => { + const acct = 'user@standardsubdomainfes.localhost:8001'; // added port to trick extension into calling the mock const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.test.key.used.pgp', { submitPubkey: false, usedPgpBefore: false }, { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); @@ -1774,7 +1783,7 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te }); await dbPage.close(); const subject = 'PWD encrypted message with FES - ID TOKEN'; - const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user@standardsubdomainfes.test:8001'); + const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user@standardsubdomainfes.localhost:8001'); await ComposePageRecipe.fillMsg(composePage, { to: 'to@example.com', bcc: 'bcc@example.com' }, subject); const fileInput = await composePage.target.$('input[type=file]') as ElementHandle; await fileInput!.uploadFile('test/samples/small.txt'); @@ -1792,18 +1801,13 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te // also see '/api/v1/message' in fes-endpoints.ts mock })); - /** - * You need the following lines in /etc/hosts: - * 127.0.0.1 standardsubdomainfes.test - * 127.0.0.1 fes.standardsubdomainfes.test - */ - ava.default('user2@standardsubdomainfes.test:8001 - PWD encrypted message with FES - Reply rendering', testWithBrowser(undefined, async (t, browser) => { - const acct = 'user2@standardsubdomainfes.test:8001'; // added port to trick extension into calling the mock + ava.default('user2@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES - Reply rendering', testWithBrowser(undefined, async (t, browser) => { + const acct = 'user2@standardsubdomainfes.localhost:8001'; // added port to trick extension into calling the mock const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.test.key.used.pgp', { submitPubkey: false, usedPgpBefore: false }, { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); const appendUrl = 'threadId=1803be2e506153d2&skipClickPrompt=___cu_false___&ignoreDraft=___cu_false___&replyMsgId=1803be3182d1937b'; - const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user2@standardsubdomainfes.test:8001', { appendUrl, hasReplyPrompt: true }); + const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user2@standardsubdomainfes.localhost:8001', { appendUrl, hasReplyPrompt: true }); await composePage.waitAndClick('@action-accept-reply-all-prompt', { delay: 2 }); // we should have 4 recipients, 2 green and 2 gray const container = (await composePage.waitAny('@container-to'))!; @@ -1846,18 +1850,13 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te await composePage.waitForContent('@recipients-preview', ' more'); })); - /** - * You need the following lines in /etc/hosts: - * 127.0.0.1 standardsubdomainfes.test - * 127.0.0.1 fes.standardsubdomainfes.test - */ - ava.default('user3@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal - pubkey recipient in bcc', testWithBrowser(undefined, async (t, browser) => { - const acct = 'user3@standardsubdomainfes.test:8001'; // added port to trick extension into calling the mock + ava.default('user3@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - pubkey recipient in bcc', testWithBrowser(undefined, async (t, browser) => { + const acct = 'user3@standardsubdomainfes.localhost:8001'; // added port to trick extension into calling the mock const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.test.key.used.pgp', { submitPubkey: false, usedPgpBefore: false }, { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); const subject = 'PWD encrypted message with FES - pubkey recipient in bcc'; - const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user3@standardsubdomainfes.test:8001'); + const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user3@standardsubdomainfes.localhost:8001'); await ComposePageRecipe.fillMsg(composePage, { to: 'to@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); await composePage.waitAndType('@input-password', 'gO0d-pwd'); await composePage.waitAndClick('@action-send', { delay: 1 }); @@ -1868,19 +1867,14 @@ export const defineComposeTests = (testVariant: TestVariant, testWithBrowser: Te // also see '/api/v1/message' in fes-endpoints.ts mock })); - /** - * You need the following lines in /etc/hosts: - * 127.0.0.1 standardsubdomainfes.test - * 127.0.0.1 fes.standardsubdomainfes.test - */ - ava.default('user4@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal - a send fails with gateway update error', testWithBrowser(undefined, async (t, browser) => { - const acct = 'user4@standardsubdomainfes.test:8001'; // added port to trick extension into calling the mock + ava.default('user4@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - a send fails with gateway update error', testWithBrowser(undefined, async (t, browser) => { + const acct = 'user4@standardsubdomainfes.localhost:8001'; // added port to trick extension into calling the mock const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.test.key.used.pgp', { submitPubkey: false, usedPgpBefore: false }, { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); const subject = 'PWD encrypted message with FES web portal - a send fails with gateway update error - ' + testVariant; const expectedNumberOfPassedMessages = (await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length; - const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.test:8001'); + const composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.localhost:8001'); await ComposePageRecipe.fillMsg(composePage, { to: 'gatewayfailure@example.com' }, subject); await composePage.waitAndType('@input-password', 'gO0d-pwd'); await composePage.waitAndClick('@action-send', { delay: 1 }); diff --git a/test/source/tests/decrypt.ts b/test/source/tests/decrypt.ts index cd9f66f5f2d..423ffe2ee0a 100644 --- a/test/source/tests/decrypt.ts +++ b/test/source/tests/decrypt.ts @@ -11,7 +11,6 @@ import { SettingsPageRecipe } from './page-recipe/settings-page-recipe'; import { TestUrls } from './../browser/test-urls'; import { TestWithBrowser } from './../test'; import { expect } from "chai"; -import { ComposePageRecipe } from './page-recipe/compose-page-recipe'; import { PageRecipe } from './page-recipe/abstract-page-recipe'; import { Buf } from '../core/buf'; @@ -29,7 +28,7 @@ export const defineDecryptTests = (testVariant: TestVariant, testWithBrowser: Te const threadId = '17d7a32a0613071d'; const acctEmail = 'flowcrypt.compatibility@gmail.com'; const inboxPage = await browser.newPage(t, TestUrls.extension(`chrome/settings/inbox/inbox.htm?acctEmail=${acctEmail}&threadId=${threadId}`)); - await inboxPage.waitForSelTestState('ready', 100); + await inboxPage.waitForSelTestState('ready'); await inboxPage.waitAll('iframe'); const pgpBlock = await inboxPage.getFrame(['pgp_block.htm']); await pgpBlock.waitForContent('@pgp-encryption', 'not encrypted'); @@ -38,6 +37,29 @@ export const defineDecryptTests = (testVariant: TestVariant, testWithBrowser: Te await inboxPage.close(); })); + ava.default(`decrypt - show warning for remote images`, testWithBrowser('compatibility', async (t, browser) => { + const threadId = '1850b93d7772173c'; + const acctEmail = 'flowcrypt.compatibility@gmail.com'; + const inboxPage = await browser.newPage(t, TestUrls.extension(`chrome/settings/inbox/inbox.htm?acctEmail=${acctEmail}&threadId=${threadId}`)); + await inboxPage.waitForSelTestState('ready'); + await inboxPage.waitAll('iframe'); + const pgpBlock = await inboxPage.getFrame(['pgp_block.htm']); + await pgpBlock.waitForContent('@pgp-block-content', '[Remote images are blocked due to security]'); + await inboxPage.close(); + })); + + ava.default(`decrypt - show inline image when user clicks show image`, testWithBrowser('compatibility', async (t, browser) => { + const threadId = '1850f9608240f758'; + const acctEmail = 'flowcrypt.compatibility@gmail.com'; + const inboxPage = await browser.newPage(t, TestUrls.extension(`chrome/settings/inbox/inbox.htm?acctEmail=${acctEmail}&threadId=${threadId}`)); + await inboxPage.waitForSelTestState('ready'); + await inboxPage.waitAll('iframe'); + const pgpBlock = await inboxPage.getFrame(['pgp_block.htm']); + await pgpBlock.waitForContent('@pgp-block-content', 'This message contains inline base64 image'); + await pgpBlock.waitAndClick('@show-inline-image'); + await inboxPage.close(); + })); + ava.default(`decrypt - outlook message with ATTxxxx encrypted email doesn't show empty attachment`, testWithBrowser('compatibility', async (t, browser) => { const threadId = '17dbdf2425ac0f29'; const acctEmail = 'flowcrypt.compatibility@gmail.com'; @@ -48,6 +70,31 @@ export const defineDecryptTests = (testVariant: TestVariant, testWithBrowser: Te await inboxPage.close(); })); + ava.default('decrypt - encrypted text inside "message" attachment is correctly decrypted', testWithBrowser('ci.tests.gmail', async (t, browser) => { + const acctEmail = 'ci.tests.gmail@flowcrypt.test'; + const key = Config.key('flowcrypt.compatibility.1pp1')!; + await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, key.armored!, key.passphrase, {}, false); + await InboxPageRecipe.checkDecryptMsg(t, browser, { + acctEmail, + threadId: '184a474fc1bd59b8', + expectedContent: 'This message contained the actual encrypted text inside a "message" attachment.' + }); + })); + + ava.default(`decrypt - render plain text for "message" attachment (which has plain text)`, testWithBrowser('ci.tests.gmail', async (t, browser) => { + const threadId = '184a87a7b32dd009'; + const acctEmail = 'ci.tests.gmail@flowcrypt.test'; + const inboxPage = await browser.newPage(t, TestUrls.extension(`chrome/settings/inbox/inbox.htm?acctEmail=${acctEmail}&threadId=${threadId}`)); + await inboxPage.waitForSelTestState('ready'); + await inboxPage.waitAll('iframe'); + expect(await inboxPage.isElementPresent('@container-attachments')).to.equal(true); + await inboxPage.waitForContent('.message.line', 'Plain message'); + // expect no pgp blocks + const urls = await inboxPage.getFramesUrls(['/chrome/elements/pgp_block.htm']); + expect(urls.length).to.equal(0); + await inboxPage.close(); + })); + ava.default(`decrypt - outlook message with ATTxxxx encrypted email is correctly decrypted`, testWithBrowser('compatibility', async (t, browser) => { const acctEmail = 'flowcrypt.compatibility@gmail.com'; await InboxPageRecipe.checkDecryptMsg(t, browser, { @@ -337,15 +384,6 @@ export const defineDecryptTests = (testVariant: TestVariant, testWithBrowser: Te }); })); - ava.default(`decrypt - [flowcrypt] remote images get replaced with a button`, testWithBrowser('compatibility', async (t, browser) => { - await BrowserRecipe.pgpBlockVerifyDecryptedContent(t, browser, { - content: ["This includes a remote image", "show image", "It should not load"], - encryption: 'encrypted', - signature: 'not signed', - params: "?frame_id=frame_obvAUTGAJU&message=-----BEGIN%20PGP%20MESSAGE-----%0AVersion%3A%20FlowCrypt%206.0.7%20Gmail%20Encryption%0AComment%3A%20Seamlessly%20send%20and%20receive%20encrypted%20email%0A%0AwcFMAzBfgamu0SA1AQ%2F%2FUx%2Bp8YjoeUUXHd9cd0rWntMM9eNkdWuOVpmURxSG%0A6ayF7lp5Tp26nDZvVmxQE0%2BYskt66vXpbT2BLjVnuhBrCiEkjvLuGMWcXLdc%0AidkaEXswVw9p31VeqrefKvMu4kCqwdHHpiWwf20PKhJQXAzXpmIjlJq1JHbE%0AVpZ5a76Qv2zh%2FxHDHvQQAGmvsjJFzjuClUKtIbqn%2BaQEx7q5zefKB2nObmQC%0AQ%2B5wup%2Bm%2BUaRZ2mwU8KVnjHizHb%2FLQXw8Ei93s0VVclHG2R9OYWZygToIJc3%0A05nk6RNQQYPSEWvsFC%2Bx%2FdkAhg9dpo0%2BPT4qnEdFQstkbzGAZXPxd4AfILPr%0A01OTfcp%2BDteZCaieFFlunwnCHjBRwA4R6zNeVt9N4lBu7Q9Tg0kso3cbkQTq%0AJckLlRQuKXIYK%2B29S6NBFsOndBBbQazGK%2FKlPYCh%2FTBRm6i%2BsQ7vbZOuX402%0Av1RviRc1HQ7Cl3ZZO4po4CtR3kDXcrERpRelUMcxKwQqmTeUQT3MNnZtOdXx%0A93SA9zqnklIuCrSkioL7g16i4cjKsFHwd5EmVdjAQCOSxXMKcbR4jE%2BmTP21%0Ae2nT%2BIIZmotruTp6Z3W4Pxh79BS8HKV8o9NxWlIjLa8xpS1YjA4l502z0vKi%0AFBUZxSC1Srdjlu6Zw4k2SntRDUASXQfMB6FlR20VV3zBwUwDvb12YPaZjcQB%0AD%2F9wEhZLgqSmrYu2dGN7xEE3lMNFuajttH3GDtan3M6LnQFe%2FAkvpFIYdS%2FH%0AgPVLZAJKgSGQySFpCrSc9LnoOlemQYbI4u97imi%2BP87WiZ4Cr%2Buv06NHBjFm%0AxDRIANTHqqatQja7X2mSXbCyXElZs8YKscz%2BaS9rK6S1m6vqmsilb2yRqw3Z%0AZr0eP4D7mpf1HZMZpKZ%2Bp%2BuV7H7FFddGEiekL7IlkyzeSZQYv5kUtXjyOwQG%0Atnx6bo2u99fG3yMa0RNK0enbMBviF5bcCWA6Z0HOfLmx9dIUYi4j%2Bp%2FG7w6z%0A0YeKMJjEFlpPNEhmeDsPJpbF41o%2B7iIzZdBYAhtxtrmZkYk%2BiMbhZtGZG5Yu%0A3v56OFdgXCvkOBMKK0oASoqzPXe0WrBsf41y4Ie97wBBLQ7OnYdR%2BQk0LWjM%0Atwjvm4GPvx3QO6sVi0sRHfRb97PB0QExw8Xk5vS0mKk9lO7pyYiTUiarjNze%0ADCTlCpyExHTNybxjUYcvDr9S9UebA4UzDQwL2V5y1GvPf5yPAV443pqQx6hW%0ApysptKiOg4Nud4b1Yfk4IAHtVesMJDMidowrdmOv1cU%2FgUe2f%2Frz14lErMYL%0AVCsQP7BIN6GkHnuPqrZybiZ02BNuOymNqsoxcQuphFV%2BNv1XyNAKSmbhtRjp%0AXhw%2BsZp0N0Fi9dKQ2ZmimK3nLNLFcQG17x7yBYzZkw04FLeKQCqHfJL%2BsE5U%0AbU%2BTeg1IQ%2FMMovXvZpJf7tNtPjx7%2BRKR0RwbRVyTLLKA6nUjOo34OJIaYHSk%0Ax1BUIC1YrXZTikDowlDNWYNpsySn05g3AFfWgRKhFzYTKX7BwDZnnWvyzi13%0Agpmh6SMbRMKaZSQ7%2FdfP67AyvBihu1B25FHQU0WEplEooMsvHONbHBECk7cg%0A%2F3FQriLkBIJ63YFpHpYBlqk%2FFtKEHioHxCu3noi7Kalkp3mSqLUjW27lmbOu%0A4YPVCw1xFupCglp5YgupuVTpnZt6FPF3uSWDipDgA3ECurBvH880ibOdPvxs%0AhIlouP2DotFvgSROgP5NKLska8a5dW5C%2BZwyEwQNEbok7vya%2BzRd6V7WExcA%0AR7cBywNLyEXgJJ%2FQa0rYd%2FHq%2FZ79HfrvVLvydk7DCRv5OomDaW8ipSI7DC5v%0AlSPLbkV6hghX7Cglv%2Bdnit2KqpzP78xoiQ4N5omUSun6ozS6aOu7zYC%2B4v2A%0AxhepY45%2B1Jc56MEinzq1gIgJ0TdzsnV8OaAnQn24X1g%2FkaWW%2Bb3tdsBfg7ec%0Ak5GLzOoMYopf4TkgJDpDX9oO8hXGFg2MoPEkhYjfsKMQd%2FOHd%2BFAOn3hl2m8%0AH5eC8lrOcaeWd%2B3Wp3wLXx5K4IWWSao1cnUhLWojFNDS2NJD3jItXzR2bTTy%0AbJlg4RgU%2Fxk2fJdMK0meKCsjlzU%2FXEcuv5aXMaUD9CM6XfBybFXyQ7eDzgm0%0AhjncOUw%2BaLsL%2FoHKffoOo53lp%2FiYDLNj35AVUScRCuhINuVUIVOZKpbUAnM6%0AuVuo%2BAgYt8ToSI7gmyNhFG7BDHirhPxFQsmW1IAv3D2g0NxflWD2f2uFralO%0A%2FQuJTaNEbKEOtsr4OKbY8Tnv9Tv%2B58Mf64D2H0KLg8vb8PWKaNA%2BfgRrWQ1V%0Af7s3yXOs6gGdK04995c1NZYsscSpN4X7XkIAmpRVp1nknY9%2FgIqlvjdBsQqI%0AKhBShbg7khtU7yeu1Seq9CTQFDFKPz%2FHG5CSx4OUVPIDnsuwxDCeTH3aNmGQ%0ALR8GVQ%2Fuu8g7eSjh7ab5w9KLyGwyX3VAEzLgdkItNu9pO%2FxPzYYBFWvzyZb4%0AbyjlxtSbe%2FvmHkItEIHZswsYRKPWWTAKqu2avqNw%2FFYI8GZvI8EPY2rxosaw%0AVVeAKn4fF07HxzVgPbWCxS3oJc5qXpUYVvR1B5nF9vHa2UEiJflJb2A7Udas%0A5LqDNcLogNWIhkPWQswIxml2bJVBV32x0YE2Prll6sr7Mtn%2F2PjDFonkebe6%0Az%2FnOOQ3JBC7rLxFjl6%2BQd8ieRKHvzkfOvjaaJEYIKb%2F1cD1mKQD4yfezVGdn%0AybLh45TTiLnWvahRNImkNuDKFGlGcfYYhifvlAVR7OAsPR9tZDJ8lx4vWhz7%0AoXdNDG2p4tzOzNoOw%2FmnuRolKnEOeMFDCKqAf%2Fh2W6YoWkp2yCAb2Ab37E4L%0AKL7zTAF%2Bp3KTpCEk58jaNAvR1TLKYMKB6ABdEQAhep7iG3vAM0wNh2LSVitX%0Ac%2BqOt3QzF5ucRchYnl33EHplwqewYPfwReSDcDZ9BdYZ65sOnj6H618RBUaU%0APEccylO1wv%2BTDwXrlFvdWBG1Gg0G7WhXuxdu2vY1PJj9z53lDZfGa0R1twsx%0A%2BU%2FBTa%2FNrJT1E9mxvUbZvUdI3eNO8LN6zokhqlj5uY2YaOj6%2BhRohMSltoRc%0AJvqXcbDYusQFhe6LjAJhRhywx57w5cqFwFp1ZAYmCay0eBoabHKrTCS6iOE8%0AS0B%2BdnnNq5vF%2BKk7Ch%2FZDLdqY0eQxupGct8vkgEOa5sLcU5GdIL4qYxskqqP%0Afj7hZkDxDWzBUhH%2Fha9GmShF46Ec%2FPDkXA%2FwZoxgJ18v%2BkZhuvl0uGPLWhjs%0ANNV6FD%2BGHFVqEEZxuaVqpVAKmeiFeeu9gErBFv3lbbbKL%2FAHcipCoslfnYib%0A826fC9NUStDxZUW4nqPPCbCBxgnlZn9GNXvjnXiGKi%2FKDVdiQYuiCvT%2FNKqn%0AfvH1X3RbMk64HzZC2Kf%2F0GLtiz0mySO2zS9vQQ4WZZRMyRcGCyAAd5R%2BY%2FjT%0ALsgp%2Bm21y3X05Oh6MVjZCLrmBsD%2B5MaI%2F3Nv%2BmXmKqg%3D%0A%3D%2F4SV%0A-----END%20PGP%20MESSAGE-----&message_id=166b194b21a0997c&senderEmail=&is_outgoing=___cu_false___&account_email=flowcrypt.compatibility%40gmail.com" - }); - })); - ava.default(`decrypt - [security] mdc - missing - error`, testWithBrowser('compatibility', async (t, browser) => { await BrowserRecipe.pgpBlockVerifyDecryptedContent(t, browser, { error: "decrypt error", @@ -473,39 +511,6 @@ XZ8r4OC6sguP/yozWlkG+7dDxsgKQVBENeG6Lw== await InboxPageRecipe.checkFinishingSession(t, browser, acctEmail, threadId); })); - ava.default('decrypt - entering pass phrase should unlock all keys that match the pass phrase', testWithBrowser('compatibility', async (t, browser) => { - const acctEmail = 'flowcrypt.compatibility@gmail.com'; - const passphrase = 'pa$$w0rd'; - await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey17AD7D07, passphrase, {}, false); - await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey0389D3A7, passphrase, {}, false); - await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testKeyMultipleSmimeCEA2D53BB9D24871, passphrase, {}, false); - const inboxPage = await browser.newPage(t, TestUrls.extensionInbox(acctEmail)); - await InboxPageRecipe.finishSessionOnInboxPage(inboxPage); - await InboxPageRecipe.checkDecryptMsg(t, browser, { - acctEmail, - threadId: '17c0e50966d7877c', - expectedContent: '1st key of of 2 keys with the same passphrase', - enterPp: { - passphrase, - isForgetPpChecked: true, - isForgetPpHidden: false - } - }); - await InboxPageRecipe.checkDecryptMsg(t, browser, { - acctEmail, - threadId: '17c0e55caaa4abb3', - expectedContent: '2nd key of of 2 keys with the same passphrase', - // passphrase for the 2nd key should not be needed because it's the same as for the 1st key - }); - // as decrypted s/mime messages are not rendered yet (#4070), let's test signing instead - const composeFrame = await InboxPageRecipe.openAndGetComposeFrame(inboxPage); - await ComposePageRecipe.fillMsg(composeFrame, { to: 'smime@recipient.com' }, 'send signed and encrypted S/MIME without attachment'); - await ComposePageRecipe.pastePublicKeyManually(composeFrame, inboxPage, 'smime@recipient.com', - testConstants.testCertificateMultipleSmimeCEA2D53BB9D24871); - await composeFrame.waitAndClick('@action-send', { delay: 2 }); - await inboxPage.waitTillGone('@container-new-message'); - })); - ava.default('decrypt - thunderbird - signedHtml verifyDetached doesn\'t duplicate PGP key section', testWithBrowser('compatibility', async (t, browser) => { const threadId = '17daefa0eb077da6'; const acctEmail = 'flowcrypt.compatibility@gmail.com'; @@ -809,6 +814,17 @@ XZ8r4OC6sguP/yozWlkG+7dDxsgKQVBENeG6Lw== await BrowserRecipe.pgpBlockVerifyDecryptedContent(t, browser, { params, content: [], encryption: '', signature: '', error: 'parse error' }); })); + ava.default('decrypt - prevent rendering of attachments from domain sources other than flowcrypt.s3.amazonaws.com1', testWithBrowser('compatibility', async (t, browser) => { + const threadId1 = '184cc6aa8e884397'; + const acctEmail = 'flowcrypt.compatibility@gmail.com'; + const inboxPage = await browser.newPage(t, TestUrls.extension(`chrome/settings/inbox/inbox.htm?acctEmail=${acctEmail}&threadId=${threadId1}`)); + await inboxPage.waitAll('iframe'); + const pgpBlock = await inboxPage.getFrame(['pgp_block.htm']); + await pgpBlock.waitForSelTestState('ready'); + await pgpBlock.waitForContent('@pgp-block-content', '[skipped attachment due to invalid url]'); + await pgpBlock.notPresent(['.preview-attachment', '@download-attachment-0']); + })); + ava.default(`decrypt - try path traversal forward slash workaround`, testWithBrowser('compatibility', async (t, browser) => { const params = "?frame_id=frame_TWloVRhvZE&message=&message_id=..\\test&senderEmail=&is_outgoing=___cu_false___&account_email=flowcrypt.compatibility%40gmail.com"; const pgpHostPage = await browser.newPage(t, `chrome/dev/ci_pgp_host_page.htm${params}`); diff --git a/test/source/tests/flaky.ts b/test/source/tests/flaky.ts index be9f21cbb33..80ca41f355e 100644 --- a/test/source/tests/flaky.ts +++ b/test/source/tests/flaky.ts @@ -143,90 +143,87 @@ export const defineFlakyTests = (testVariant: TestVariant, testWithBrowser: Test await settingsPage.close(); })); - /** - * You need the following lines in /etc/hosts: - * 127.0.0.1 standardsubdomainfes.test - * 127.0.0.1 fes.standardsubdomainfes.test - */ - ava.default('user4@standardsubdomainfes.test:8001 - PWD encrypted message with FES web portal - some sends fail with BadRequest error', testWithBrowser(undefined, async (t, browser) => { - const acct = 'user4@standardsubdomainfes.test:8001'; // added port to trick extension into calling the mock - const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); - await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.test.key.used.pgp', { submitPubkey: false, usedPgpBefore: false }, - { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); - // add a name to one of the contacts - const dbPage = await browser.newPage(t, TestUrls.extension('chrome/dev/ci_unit_test.htm')); - await dbPage.page.evaluate(async () => { - // eslint-disable-next-line @typescript-eslint/no-explicit-any - const db = await (window as any).ContactStore.dbOpen(); - // eslint-disable-next-line @typescript-eslint/no-explicit-any - await (window as any).ContactStore.update(db, 'cc@example.com', { name: 'Mr Cc' }); - }); - await dbPage.close(); - const subject = 'PWD encrypted message with FES web portal - some sends fail with BadRequest error - ' + testVariant; - let expectedNumberOfPassedMessages = (await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length; - // 1. vague Gmail error with partial success - let composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.test:8001'); - await ComposePageRecipe.fillMsg(composePage, { to: 'to@example.com', cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal('confirm', 'cancel', - 'Messages to some recipients were sent successfully, while messages to flowcrypt.compatibility@gmail.com, Mr Cc ' + - 'encountered error(s) from Gmail. Please help us improve FlowCrypt by reporting the error to us.'); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); - // 2. vague Gmail error with all failures - composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.test:8001'); - await ComposePageRecipe.fillMsg(composePage, { cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal('confirm', 'cancel', - 'Google returned an error when sending message. ' + - 'Please help us improve FlowCrypt by reporting the error to us.'); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages - // 3. "invalid To" Gmail error with partial success - composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.test:8001'); - await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'to@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal('error', 'confirm', - 'Messages to some recipients were sent successfully, while messages to invalid@example.com ' + - 'encountered error(s) from Gmail: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.'); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); - // 4. "invalid To" Gmail error with all failures - composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.test:8001'); - await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'cc@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal('error', 'confirm', - 'Error from google: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.'); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages - // 5. "RequestTimeout" error with partial success - composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.test:8001'); - await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'to@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal('error', 'confirm', - 'Messages to some recipients were sent successfully, while messages to timeout@example.com ' + - 'encountered network errors. Please check your internet connection and try again.'); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); - // 6. "RequestTimeout" error with all failures - composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.test:8001'); - await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'cc@example.com' }, subject); - await composePage.waitAndType('@input-password', 'gO0d-pwd'); - await composePage.waitAndClick('@action-send', { delay: 1 }); - await composePage.waitAndRespondToModal('error', 'confirm', - 'Could not send message due to network error. Please check your internet connection and try again. ' + - '(This may also be caused by missing extension permissions).'); - await composePage.close(); - expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages - // this test is using PwdEncryptedMessageWithFesReplyBadRequestTestStrategy to check sent result based on subject - // "PWD encrypted message with FES web portal - some sends fail with BadRequest error" - // also see '/api/v1/message' in fes-endpoints.ts mock - })); + ava.default( + 'user4@standardsubdomainfes.localhost:8001 - PWD encrypted message with FES web portal - some sends fail with BadRequest error', + testWithBrowser(undefined, async (t, browser) => { + const acct = 'user4@standardsubdomainfes.localhost:8001'; // added port to trick extension into calling the mock + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); + await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.test.key.used.pgp', { submitPubkey: false, usedPgpBefore: false }, + { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); + // add a name to one of the contacts + const dbPage = await browser.newPage(t, TestUrls.extension('chrome/dev/ci_unit_test.htm')); + await dbPage.page.evaluate(async () => { + // eslint-disable-next-line @typescript-eslint/no-explicit-any + const db = await (window as any).ContactStore.dbOpen(); + // eslint-disable-next-line @typescript-eslint/no-explicit-any + await (window as any).ContactStore.update(db, 'cc@example.com', { name: 'Mr Cc' }); + }); + await dbPage.close(); + const subject = 'PWD encrypted message with FES web portal - some sends fail with BadRequest error - ' + testVariant; + let expectedNumberOfPassedMessages = (await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length; + // 1. vague Gmail error with partial success + let composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.localhost:8001'); + await ComposePageRecipe.fillMsg(composePage, { to: 'to@example.com', cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal('confirm', 'cancel', + 'Messages to some recipients were sent successfully, while messages to flowcrypt.compatibility@gmail.com, Mr Cc ' + + 'encountered error(s) from Gmail. Please help us improve FlowCrypt by reporting the error to us.'); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); + // 2. vague Gmail error with all failures + composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.localhost:8001'); + await ComposePageRecipe.fillMsg(composePage, { cc: 'cc@example.com', bcc: 'flowcrypt.compatibility@gmail.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal('confirm', 'cancel', + 'Google returned an error when sending message. ' + + 'Please help us improve FlowCrypt by reporting the error to us.'); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages + // 3. "invalid To" Gmail error with partial success + composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.localhost:8001'); + await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'to@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal('error', 'confirm', + 'Messages to some recipients were sent successfully, while messages to invalid@example.com ' + + 'encountered error(s) from Gmail: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.'); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); + // 4. "invalid To" Gmail error with all failures + composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.localhost:8001'); + await ComposePageRecipe.fillMsg(composePage, { to: 'invalid@example.com', cc: 'cc@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal('error', 'confirm', + 'Error from google: Invalid recipients\n\nPlease remove recipients, add them back and re-send the message.'); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages + // 5. "RequestTimeout" error with partial success + composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.localhost:8001'); + await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'to@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal('error', 'confirm', + 'Messages to some recipients were sent successfully, while messages to timeout@example.com ' + + 'encountered network errors. Please check your internet connection and try again.'); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(++expectedNumberOfPassedMessages); + // 6. "RequestTimeout" error with all failures + composePage = await ComposePageRecipe.openStandalone(t, browser, 'user4@standardsubdomainfes.localhost:8001'); + await ComposePageRecipe.fillMsg(composePage, { to: 'timeout@example.com', cc: 'cc@example.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + await composePage.waitAndRespondToModal('error', 'confirm', + 'Could not send message due to network error. Please check your internet connection and try again. ' + + '(This may also be caused by missing extension permissions).'); + await composePage.close(); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(expectedNumberOfPassedMessages); // + 0 messages + // this test is using PwdEncryptedMessageWithFesReplyBadRequestTestStrategy to check sent result based on subject + // "PWD encrypted message with FES web portal - some sends fail with BadRequest error" + // also see '/api/v1/message' in fes-endpoints.ts mock + })); ava.default('user@forbid-storing-passphrase-client-configuration.flowcrypt.test - do not store passphrase', testWithBrowser(undefined, async (t, browser) => { const acctEmail = 'user@forbid-storing-passphrase-client-configuration.flowcrypt.test'; @@ -259,6 +256,21 @@ export const defineFlakyTests = (testVariant: TestVariant, testWithBrowser: Test await ComposePageRecipe.sendAndClose(composePage); })); + ava.default('compose - test compose after reconnect account', testWithBrowser('ci.tests.gmail', async (t, browser) => { + const acct = 'ci.tests.gmail@flowcrypt.test'; + const composePage = await ComposePageRecipe.openStandalone(t, browser, 'compose'); + await Util.wipeGoogleTokensUsingExperimentalSettingsPage(t, browser, acct); + await ComposePageRecipe.showRecipientInput(composePage); + const subject = 'PWD encrypted message after reconnect account'; + await ComposePageRecipe.fillMsg(composePage, { to: 'test@email.com' }, subject); + await composePage.waitAndType('@input-password', 'gO0d-pwd'); + await composePage.waitAndClick('@action-send', { delay: 1 }); + const oauthPopup = await browser.newPageTriggeredBy(t, () => composePage.waitAndRespondToModal('confirm', 'confirm', 'Please log in with FlowCrypt to continue')); + await OauthPageRecipe.google(t, oauthPopup, acct, 'approve'); + await ComposePageRecipe.closed(composePage); + expect((await GoogleData.withInitializedData(acct)).searchMessagesBySubject(subject).length).to.equal(1); + })); + ava.default('with attachments + shows progress %', testWithBrowser('compatibility', async (t, browser) => { const composePage = await ComposePageRecipe.openStandalone(t, browser, 'compatibility'); await ComposePageRecipe.fillMsg(composePage, { to: 'human@flowcrypt.com' }, 'with files'); @@ -359,6 +371,38 @@ export const defineFlakyTests = (testVariant: TestVariant, testWithBrowser: Test t.pass(); }); + ava.default('decrypt - entering pass phrase should unlock all keys that match the pass phrase', testWithBrowser('compatibility', async (t, browser) => { + const acctEmail = 'flowcrypt.compatibility@gmail.com'; + const passphrase = 'pa$$w0rd'; + await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey17AD7D07, passphrase, {}, false); + await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testkey0389D3A7, passphrase, {}, false); + await SettingsPageRecipe.addKeyTest(t, browser, acctEmail, testConstants.testKeyMultipleSmimeCEA2D53BB9D24871, passphrase, {}, false); + const inboxPage = await browser.newPage(t, TestUrls.extensionInbox(acctEmail)); + await InboxPageRecipe.finishSessionOnInboxPage(inboxPage); + await InboxPageRecipe.checkDecryptMsg(t, browser, { + acctEmail, + threadId: '17c0e50966d7877c', + expectedContent: '1st key of of 2 keys with the same passphrase', + enterPp: { + passphrase, + isForgetPpChecked: true, + isForgetPpHidden: false + } + }); + await InboxPageRecipe.checkDecryptMsg(t, browser, { + acctEmail, + threadId: '17c0e55caaa4abb3', + expectedContent: '2nd key of of 2 keys with the same passphrase', + // passphrase for the 2nd key should not be needed because it's the same as for the 1st key + }); + // as decrypted s/mime messages are not rendered yet (#4070), let's test signing instead + const composeFrame = await InboxPageRecipe.openAndGetComposeFrame(inboxPage); + await ComposePageRecipe.fillMsg(composeFrame, { to: 'smime@recipient.com' }, 'send signed and encrypted S/MIME without attachment'); + await ComposePageRecipe.pastePublicKeyManually(composeFrame, inboxPage, 'smime@recipient.com', + testConstants.testCertificateMultipleSmimeCEA2D53BB9D24871); + await composeFrame.waitAndClick('@action-send', { delay: 2 }); + await inboxPage.waitTillGone('@container-new-message'); + })); } }; diff --git a/test/source/tests/gmail.ts b/test/source/tests/gmail.ts index 07f1fbd3a70..8b57d469152 100644 --- a/test/source/tests/gmail.ts +++ b/test/source/tests/gmail.ts @@ -36,11 +36,11 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test // Total compose frame count composeFrameCount }: - { - isReplyPromptAccepted?: boolean, - composeFrameIndex?: number, - composeFrameCount?: number - } = {} + { + isReplyPromptAccepted?: boolean, + composeFrameIndex?: number, + composeFrameCount?: number + } = {} ) => { const urls = await gmailPage.getFramesUrls(['/chrome/elements/compose.htm'], { sleep: 0 }); expect(urls.length).to.equal(composeFrameCount ?? 1); @@ -379,6 +379,30 @@ export const defineGmailTests = (testVariant: TestVariant, testWithBrowser: Test await gmailPage.close(); })); + ava.default(`mail.google.com - encrypted text inside "message" attachment`, testWithBrowser(undefined, async (t, browser) => { + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, 'ci.tests.gmail@flowcrypt.dev'); + await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.compatibility.1pp1', { submitPubkey: false, usedPgpBefore: true, }, + { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); + const gmailPage = await openGmailPage(t, browser); + await gotoGmailPage(gmailPage, '/FMfcgzGrbHprlHvtTJscCJQpZcqrKQbg'); + await Util.sleep(5); + await gmailPage.waitAll('iframe'); + expect(await gmailPage.isElementPresent('@container-attachments')).to.equal(false); + await gmailPage.waitAll(['.aZi'], { visible: false }); + await gmailPage.close(); + })); + + ava.default(`mail.google.com - render plain text for "message" attachment (which has plain text)`, testWithBrowser('ci.tests.gmail', async (t, browser) => { + const gmailPage = await openGmailPage(t, browser); + await gotoGmailPage(gmailPage, '/FMfcgzGrbHrBdFGBXqpFZvSkcQpKkvrM'); + await Util.sleep(5); + await gmailPage.waitForContent('.a3s', 'Plain message'); + expect(await gmailPage.isElementPresent('div.aQH')).to.equal(true); // gmail attachment container + // expect no pgp blocks + const urls = await gmailPage.getFramesUrls(['/chrome/elements/pgp_block.htm']); + expect(urls.length).to.equal(0); + })); + ava.default('mail.google.com - pubkey file gets rendered', testWithBrowser('ci.tests.gmail', async (t, browser) => { const gmailPage = await openGmailPage(t, browser); await gotoGmailPage(gmailPage, '/FMfcgzGkbDXBWCgTcMJlmBtfNxrbzTTn'); diff --git a/test/source/tests/page-recipe/oauth-page-recipe.ts b/test/source/tests/page-recipe/oauth-page-recipe.ts index bcf8d0d187d..8fb19704e08 100644 --- a/test/source/tests/page-recipe/oauth-page-recipe.ts +++ b/test/source/tests/page-recipe/oauth-page-recipe.ts @@ -2,7 +2,7 @@ import { Config, Util } from '../../util'; import { AvaContext } from '../tooling/'; -import { ControllablePage } from '../../browser'; +import { ControllablePage, TIMEOUT_PAGE_LOAD } from '../../browser'; import { PageRecipe } from './abstract-page-recipe'; import { Url } from '../../core/common'; @@ -37,10 +37,15 @@ export class OauthPageRecipe extends PageRecipe { public static google = async (t: AvaContext, oauthPage: ControllablePage, acctEmail: string, action: "close" | "deny" | "approve" | "login" | "login_with_invalid_state"): Promise => { try { - const isMock = oauthPage.target.url().includes('localhost') || oauthPage.target.url().includes('google.mock.flowcryptlocal.test'); + const isMock = oauthPage.target.url().includes('localhost') || oauthPage.target.url().includes('google.mock.localhost'); if (isMock) { await OauthPageRecipe.mock(t, oauthPage, acctEmail, action); return; + } else { + await Promise.race([ + oauthPage.page.waitForNavigation({ waitUntil: 'domcontentloaded', timeout: TIMEOUT_PAGE_LOAD * 1000 }), + oauthPage.page.waitForNavigation({ waitUntil: 'load', timeout: TIMEOUT_PAGE_LOAD * 1000 }) + ]); } } catch (e) { if (String(e).includes('page has been closed')) { @@ -61,7 +66,10 @@ export class OauthPageRecipe extends PageRecipe { try { const alreadyLoggedSelector = '.w6VTHd, .wLBAL'; const alreadyLoggedChooseOtherAccountSelector = '.bLzI3e, .BHzsHc'; - await oauthPage.waitAny(`#Email, #submit_approve_access, #identifierId, ${alreadyLoggedSelector}, #profileIdentifier`, { timeout: 45 }); + await oauthPage.waitAny( + `#Email, ${selectors.googleApproveBtn}, ${selectors.googleEmailInput}, ${alreadyLoggedSelector}, #profileIdentifier, ${selectors.auth0username}`, + { timeout: 45 } + ); if (await oauthPage.target.$(selectors.googleEmailInput) !== null) { // 2017-style login await oauthPage.waitAll(selectors.googleEmailInput, { timeout: OauthPageRecipe.longTimeout }); await oauthPage.waitAndType(selectors.googleEmailInput, acctEmail, { delay: 2 }); @@ -94,6 +102,7 @@ export class OauthPageRecipe extends PageRecipe { await oauthPage.waitAndType(selectors.auth0password, acctPassword); } await oauthPage.waitForNavigationIfAny(() => oauthPage.waitAndClick(selectors.auth0loginBtn)); + await oauthPage.waitAndClick(alreadyLoggedSelector, { delay: 1 }); } await Util.sleep(1); await oauthPage.waitAll(selectors.googleApproveBtn); // if succeeds, we are logged in and presented with approve/deny choice diff --git a/test/source/tests/settings.ts b/test/source/tests/settings.ts index 0baa81ebbb6..2b8d097d409 100644 --- a/test/source/tests/settings.ts +++ b/test/source/tests/settings.ts @@ -25,7 +25,7 @@ import { OpenPGPKey } from '../core/crypto/pgp/openpgp-key'; import { BrowserHandle } from '../browser'; import { AvaContext } from './tooling'; import { mockBackendData } from '../mock/backend/backend-endpoints'; -import { ClientConfiguration } from '../mock/backend/backend-data'; +import { ClientConfiguration, keyManagerAutogenRules } from '../mock/backend/backend-data'; import { HttpClientErr, Status } from '../mock/lib/api'; // tslint:disable:no-blank-lines-func @@ -351,11 +351,11 @@ export const defineSettingsTests = (testVariant: TestVariant, testWithBrowser: T await SettingsPageRecipe.ready(settingsPage); await SettingsPageRecipe.toggleScreen(settingsPage, 'additional'); await settingsPage.waitAll('@action-open-add-key-page'); - await settingsPage.waitAndClick('@action-remove-key'); + await settingsPage.waitAndClick('@action-remove-key-0'); await settingsPage.page.waitForNavigation({ waitUntil: 'networkidle0' }); await Util.sleep(1); await settingsPage.waitAll('@action-open-add-key-page'); - await settingsPage.notPresent('@action-remove-key'); + await settingsPage.notPresent('@action-remove-key-0'); })); ava.default('settings - my key page - privileged frames and action buttons should be hidden when using key manager test', testWithBrowser(undefined, async (t, browser) => { @@ -370,7 +370,7 @@ export const defineSettingsTests = (testVariant: TestVariant, testWithBrowser: T await myKeyFrame.notPresent('@action-update-prv'); await myKeyFrame.notPresent('@action-revoke-certificate'); await myKeyFrame.waitForContent('@label-download-prv', 'THIS PRIVATE KEY IS MANAGED BY EMAIL KEY MANAGER'); - await settingsPage.notPresent('@action-remove-key'); + await settingsPage.notPresent('@action-remove-key-0'); const fingerprint = await myKeyFrame.readHtml('@content-fingerprint'); // test for direct access at my_key_update.htm const myKeyUpdateFrame = await browser.newPage(t, TestUrls.extension(`chrome/settings/modules/my_key_update.htm?placement=settings&acctEmail=${acct}&fingerprint=${fingerprint}`)); @@ -970,6 +970,57 @@ export const defineSettingsTests = (testVariant: TestVariant, testWithBrowser: T await settingsPage.close(); })); + ava.default('settings - ensure gracious behavior & ui should remain functional when updating client configuration', testWithBrowser(undefined, async (t, browser) => { + const acct = 'test-update@settings.flowcrypt.test'; + mockBackendData.clientConfigurationByAcctEmail[acct] = keyManagerAutogenRules; + const setupPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); + await SetupPageRecipe.autoSetupWithEKM(setupPage); + const { + cryptup_testupdatesettingsflowcrypttest_rules: rules1 + } = await setupPage.getFromLocalStorage([ + 'cryptup_testupdatesettingsflowcrypttest_rules' + ]); + const clientConfiguration1 = rules1 as ClientConfiguration; + expect(clientConfiguration1.flags).to.eql([ + 'NO_PRV_BACKUP', + 'ENFORCE_ATTESTER_SUBMIT', + 'PRV_AUTOIMPORT_OR_AUTOGEN', + 'PASS_PHRASE_QUIET_AUTOGEN', + 'DEFAULT_REMEMBER_PASS_PHRASE']); + expect(clientConfiguration1.disallow_attester_search_for_domains).to.eql([]); + expect(clientConfiguration1.enforce_keygen_algo).to.equal('rsa2048'); + expect(clientConfiguration1.key_manager_url).to.equal('https://localhost:8001/flowcrypt-email-key-manager'); + const accessToken = await BrowserRecipe.getGoogleAccessToken(setupPage, acct); + await setupPage.close(); + // Set invalid client configuration and check if it ensures gracious behavior & ui remain functional + mockBackendData.clientConfigurationByAcctEmail[acct] = { + // flags is required but don't return it (to mock invalid client configuration) + key_manager_url: 'https://localhost:8001/flowcrypt-email-key-manager' + }; + const extraAuthHeaders = { Authorization: `Bearer ${accessToken}` }; + const gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); + const errorMsg = 'Failed to update FlowCrypt Client Configuration: Missing client configuration flags.'; + await PageRecipe.waitForToastToAppearAndDisappear(gmailPage, errorMsg); + // Ensure previous client configuration remains same + const settingsPage = await browser.newPage(t, TestUrls.extensionSettings(acct)); + await PageRecipe.waitForToastToAppearAndDisappear(settingsPage, errorMsg); + const { + cryptup_testupdatesettingsflowcrypttest_rules: rules2 + } = await settingsPage.getFromLocalStorage([ + 'cryptup_testupdatesettingsflowcrypttest_rules' + ]); + const clientConfiguration2 = rules2 as ClientConfiguration; + expect(clientConfiguration2.flags).to.eql([ + 'NO_PRV_BACKUP', + 'ENFORCE_ATTESTER_SUBMIT', + 'PRV_AUTOIMPORT_OR_AUTOGEN', + 'PASS_PHRASE_QUIET_AUTOGEN', + 'DEFAULT_REMEMBER_PASS_PHRASE']); + expect(clientConfiguration2.disallow_attester_search_for_domains).to.eql([]); + expect(clientConfiguration2.enforce_keygen_algo).to.equal('rsa2048'); + expect(clientConfiguration2.key_manager_url).to.equal('https://localhost:8001/flowcrypt-email-key-manager'); + })); + ava.default('settings - client configuration gets updated on settings and content script reloads', testWithBrowser(undefined, async (t, browser) => { const acct = 'settings@settings.flowcrypt.test'; // set up the client configuration returned for the account @@ -1155,6 +1206,26 @@ export const defineSettingsTests = (testVariant: TestVariant, testWithBrowser: T expect(savedPassphrase2).to.be.an('undefined'); })); + ava.default('settings - password messages\' expiry settings shouldn\'t be available for FES users', testWithBrowser('compatibility', async (t, browser) => { + const acct1 = 'flowcrypt.compatibility@gmail.com'; + const settingsPage = await browser.newPage(t, TestUrls.extensionSettings('flowcrypt.compatibility@gmail.com')); + const securitySettingsFrame1 = await SettingsPageRecipe.awaitNewPageFrame(settingsPage, '@action-open-security-page', ['security.htm']); + expect(await securitySettingsFrame1.isElementVisible('@container-password-messages-expiry')).to.equal(true); + await SettingsPageRecipe.closeDialog(settingsPage); + await SettingsPageRecipe.toggleScreen(settingsPage, 'additional'); + const experimentalFrame = await SettingsPageRecipe.awaitNewPageFrame(settingsPage, '@action-open-module-experimental', ['experimental.htm']); + await experimentalFrame.waitAndClick('@action-reset-account'); + await experimentalFrame.waitAndRespondToModal('confirm', 'confirm', `This will remove all your FlowCrypt settings for ${acct1}`); + await experimentalFrame.waitAndRespondToModal('confirm', 'confirm', 'Proceed to reset? Don\'t come back telling me I didn\'t warn you.'); + await settingsPage.close(); + const acct2 = 'settings@key-manager-autogen.flowcrypt.test'; + const settingsPage1 = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct2); + await SetupPageRecipe.autoSetupWithEKM(settingsPage1); + const securitySettingsFrame = await SettingsPageRecipe.awaitNewPageFrame(settingsPage1, '@action-open-security-page', ['security.htm']); + expect(await securitySettingsFrame.isElementVisible('@container-password-messages-expiry')).to.equal(false); + await settingsPage1.close(); + })); + ava.default.todo('settings - change passphrase - mismatch curent pp'); ava.default.todo('settings - change passphrase - mismatch new pp'); diff --git a/test/source/tests/setup.ts b/test/source/tests/setup.ts index 80e26df80ea..540b24ad395 100644 --- a/test/source/tests/setup.ts +++ b/test/source/tests/setup.ts @@ -2,14 +2,14 @@ import * as ava from 'ava'; -import { TestVariant, Util } from './../util'; +import { Config, TestVariant, Util } from './../util'; import { SetupPageRecipe } from './page-recipe/setup-page-recipe'; import { TestWithBrowser } from './../test'; import { expect } from 'chai'; import { SettingsPageRecipe } from './page-recipe/settings-page-recipe'; import { ComposePageRecipe } from './page-recipe/compose-page-recipe'; import { Str, emailKeyIndex } from './../core/common'; -import { MOCK_KM_LAST_INSERTED_KEY, MOCK_KM_UPDATING_KEY } from './../mock/key-manager/key-manager-endpoints'; +import { MOCK_KM_LAST_INSERTED_KEY, MOCK_KM_KEYS } from './../mock/key-manager/key-manager-endpoints'; import { MOCK_ATTESTER_LAST_INSERTED_PUB } from './../mock/attester/attester-endpoints'; import { BrowserRecipe } from './tooling/browser-recipe'; import { Key, KeyInfoWithIdentity, KeyUtil } from '../core/crypto/key'; @@ -20,6 +20,7 @@ import { TestUrls } from '../browser/test-urls'; import { BrowserHandle, ControllablePage } from '../browser'; import { OauthPageRecipe } from './page-recipe/oauth-page-recipe'; import { AvaContext } from './tooling'; +import { opgp } from '../core/crypto/pgp/openpgpjs-custom'; // tslint:disable:no-blank-lines-func // tslint:disable:no-unused-expression @@ -157,67 +158,7 @@ export const defineSetupTests = (testVariant: TestVariant, testWithBrowser: Test // `!this.users.length` condition is removed from the Key constructor. ava.default.failing('setup - import key - fix uids', testWithBrowser(undefined, async (t, browser) => { const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, 'flowcrypt.test.key.imported@gmail.com'); - await SetupPageRecipe.manualEnter(settingsPage, 'unused', { - submitPubkey: false, fixKey: true, - key: { - title: 'UIDless key', - armored: `-----BEGIN PGP PRIVATE KEY BLOCK----- -xcMFBF8/lc8BCACwwWWyNdfZ9Qjz8zc4sFGNfHXITscT7WCMuXgC2BbFwiSD -52+Z6fIKaaMFP07MOy8g3PsrW8rrM6j9ew4fh6Kr6taD5JtZfWEWxSnmfl8T -MqbfcGklJZDyqbSlRBHh53ea4fZe/wCiaL2qhME9Pa7M+w/AiCT1LuXUBiKp -oCLVn1PFf760vdsz5CD+kpzBIZ45P6zZxnR/P6zLsKjr5nERlIDZ1gWtctx9 -9ZEEVBrgnEE4dBIT1W/M/XbEwsKn1HGOyTBvzeEfM863uW0V9HKmjilbMF2P -fJ583t1HzuhA7IewcgX/VGC4QKMnukUpRhJQPlcVFSy0zjD9zQYIh437ABEB -AAH+CQMIblUClAvPYEvgLJlwFM3vC1LLOtMvegEdpUDVA0rpZLASe9RoyEbB -PGue+yaxxu06N20fsqIxaBh3+uU2ZVfcEre/5XNCj6QxHzqSbclMyHUyVHlv -/G308yKMyjvwj3mx1hNY5frDb7Pop4ZSftpx1R3tXU1DC1DGy+3Whp41BKAF -ahSQ5oK2VjUFqdoej6p46vt0pt9JOsX7T2eX7Z7TcPoJPNZ0rBDYJDV4RVYk -tdgA2P4mfbjHZOquexzRgGY9Pn7X/NciUrbmfA6sxyR21aG0xAXMk91bwPDs -SEEj7ikpIlt7F87yafzwS4JFPzuhhGpZjK1f6t24fAAmufKCdt+IEV4EgkBI -QWrfUUAXytHIPFyP3z4gcIitmx10DqArxhHeR0sKjtAjOKrMP0qBiQAG6cH+ -y4CdRiBiuEDTazgePzIDJMgIjmWH/hxl5puoEKkQAR9kiiU0bDtphSAQ5GXw -c/1WhYacYWJytUM+uUWMFAdryd93YmRew1kYxqdZn5AywzOOAbTWD6Q2GME5 -0o0Adfw4CopT2VxsbRq4X74DPtXnReyFGd0167IV3Y8HToHyM4gJxxMVXF3G -TNW7CSq2L53kklynLtBnAuJKwunR8my7Sm+CX/errsXpq/u3QGZDeHlAh8ul -rHeqOTZwEqGHxHb1FcQJ+1QQohrwJp2hHKXxgZyGQH8ykTZyNpPAiqkhcl9O -DJdxq4Ke6wistyzF/sRGRcaXaLHZ8dKS8TIjjzGuMWMaZtBO+6EqIE5JgEHe -t+SdnMeEZ9kDtWx2+eTb/j5IFuIPlWjRNndad3qpw17wvLufSUs06Pjd5O7q -3k38hvPHNpCyWWsLnddnCGJZwH5uXCsfKqrO1JkY+0gJISxQ0ZNvMCki2tpZ -k3ByPEnFoT4c6f8eJMQhODqC8Do9xrTHwwYEXz+VzwEIAKp98eVpCy1lIu26 -HdR5CYlQ5aVhqOVPlk1gWqwQwBBOykj3t3nJtA2tS/qgSgbNtk1bf7KSPUKI -E8vBGZ/uHCtC9B19ytZxHI51TQtTJgbOkuRkq7KizB+ZZ1TPwrb4HyDxtw4L -K6kBA0vhvOZeWh4XD7CPSjN457eCaKjnaD6HuvvTin4EVJ9G6B9Ioi6Oyi98 -PB0JA3dpPY4cx/3eggx18cAPeZwiO7vIy0VHtq/G8Obf2Tzowmz1vsgTm+fV -piZ8lQlQkNBn5Z9/mayZ4bMA1EGaQGzfzS+r4AYP+/UxXRCMlwZ3lt7YYnKI -5lIZX73TwXzuMwFqGEevIJzD9YkAEQEAAf4JAwhHFiWWy6b0muDxhFu5N7oX -lhSfbD+RSvezCU8xpDHbkvoOZRC21bKJ1jmkvbC/KKAlxNz5UYJ/OFtffAok -f0aTlkrNvPxN9apqDgwvsjzC10//3b9BzHjds2rrpGHKjzyapAVkEl0PGWCR -VPdfjC/f5t7GMzOsSNmTqHVS+aCX8aA48BKkjDjFOUjpLGSqVPxoMTe0gUpa -NxgJhIb5RZ+6JjbmWooZ4nw/GroUGYfupRr4TG3TYVVGXCHN+/CEClyhJDCm -sqc1ZhdarNINGVndzz/i5sBbuNMnph6j6Mh72duseSEiOxYZ0iOrwNosC0NS -qDHA+jBHyP405U8N6V1EBKf3Z+C3+vqSxiR37JkwWcaXEDoJm4oNSI6yA1aa -8QJIcUMEapfoCmA0alKzLvng5wLCEC82MvPMezkF1O6vBXCMBJs9lEGg/61K -wkiIpz2FEdulWe7Hca66KTIHWLcd0X1mF7L7XK25UW7+1CrX0cqMEhXi1wGS -SbqKIVA5bEbwNo1VgENgF0NnsR7Q8H+94k0lems8vw4xS98ogVqFdGTmGF0t -ijE4yf4M9jt7LYWGfru2DDVIHf+K7L+DuOqcjBVXVIy0x+NDSYBnLgIYujsF -5tMv33SfE17F/CHJDAujY5yTxuXDdzMmxYahsg6vx/fbXZVwm2RFpxCzI6pV -E/YWhOFMknNHVpiqvQ91Y7nOJlHQAe9RmsGcxng0bwsE1J277JozUr5PNXA9 -ZDPVG7/3nHnUnNwnXupHAsiYW4aN/uFUXg5CoArXvj2SHjWQSBMwWDQK9jC5 -YVzi15D9Jt3xYDXpDbSEf8N+d8C31Jx3QedDi/ei5xs/9CJ+DqbBxRUW04jj -r8mew9pM2+gpDS5DoNLSBJ1vn3OIRLnCudmSJBHs3NMh85qF07bc1+sAozpZ -vM7CwF8EGAEIAAkFAl8/lc8CGwwACgkQKBMN0dHENohRNAf/Z5G5pySJe4tk -G1pGQOLjZms08e1KGQlbRtZR8WN2ySCe3Pyla/R3KQRJBQS6V926GKnvsOZC -3CWVKHDcn1Rx2uV3GH8VWOHfT+EjQI7zCoQAppVEX4uJ4BCxP5Z9CgSxL8zH -31AHwLEtCqDfeZf8dttihfafyAUFKCCrN5R6cP2AtUlRDE1XRdTJ8zRk4mRX -81r0vXC1Xfs1zBy3YnDIJVJcEro9v7yOn/5WBtQT/jnBvJZ/gBieolgXUrRb -V5PJ0lZPFfMdYjjYR+i7j3+/j59kd1Wuz+6I572J+j4lWlPIvGk2V+rzzHqK -CciXuhqnLwoVF5/uXMYffVtfl/OU+w== -=EqcV ------END PGP PRIVATE KEY BLOCK-----`, - passphrase: 'correct horse battery staple', - longid: '123', - } - }); + await SetupPageRecipe.manualEnter(settingsPage, 'uid.less.key', { submitPubkey: false, fixKey: true }); }, 'FAILING')); ava.default('setup - import key - warning on primary has no secret', testWithBrowser(undefined, async (t, browser) => { @@ -429,30 +370,30 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== const gmailPage = await openMockGmailPage(t, browser, acctEmail); await gmailPage.waitAndClick('@action-secure-compose'); // Check reconnect auth notification - await gmailPage.waitForContent('@webmail-notification', 'Please reconnect FlowCrypt to your Gmail Account.'); + await gmailPage.waitForContent('@webmail-notification-setup', 'Please reconnect FlowCrypt to your Gmail Account.'); let oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage.waitAndClick('@action-reconnect-account')); // mock api will return missing scopes await OauthPageRecipe.mock(t, oauthPopup, acctEmail, 'missing_permission'); // Check missing permission notification - await gmailPage.waitForContent('@webmail-notification', 'Connection successful. Please also add missing permissions'); + await gmailPage.waitForContent('@webmail-notification-setup', 'Connection successful. Please also add missing permissions'); oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage.waitAndClick('@action-add-missing-permission')); await OauthPageRecipe.mock(t, oauthPopup, acctEmail, 'approve'); // after successful reauth, check if connection is successful - await gmailPage.waitForContent('@webmail-notification', 'Connected successfully. You may need to reload the tab.'); + await gmailPage.waitForContent('@webmail-notification-setup', 'Connected successfully. You may need to reload the tab.'); // reload and test that it has no more notifications await gmailPage.page.reload(); await gmailPage.waitAndClick('@action-secure-compose'); await Util.sleep(2); - await gmailPage.notPresent(['@webmail-notification']); + await gmailPage.notPresent(['@webmail-notification-setup']); })); ava.default('mail.google.com - success notif after setup, click hides it, does not re-appear + offers to reauth', testWithBrowser('compatibility', async (t, browser) => { const acct = 'flowcrypt.compatibility@gmail.com'; const gmailPage = await openMockGmailPage(t, browser, acct); - await gmailPage.waitAll(['@webmail-notification', '@notification-successfully-setup-action-close']); + await gmailPage.waitAll(['@webmail-notification-setup', '@notification-successfully-setup-action-close']); await gmailPage.waitAndClick('@notification-successfully-setup-action-close', { confirmGone: true }); await gmailPage.page.reload(); - await gmailPage.notPresent(['@webmail-notification', '@notification-setup-action-close', '@notification-successfully-setup-action-close']); + await gmailPage.notPresent(['@webmail-notification-setup', '@notification-setup-action-close', '@notification-successfully-setup-action-close']); // below test that can re-auth after lost access (simulating situation when user changed password on google) await Util.wipeGoogleTokensUsingExperimentalSettingsPage(t, browser, acct); const settingsPage = await browser.newPage(t, TestUrls.extensionSettings(acct)); @@ -461,28 +402,28 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== // // opening secure compose should trigger an api call which causes a reconnect notification await gmailPage.page.reload(); await gmailPage.waitAndClick('@action-secure-compose'); - await gmailPage.waitAll(['@webmail-notification', '@action-reconnect-account']); + await gmailPage.waitAll(['@webmail-notification-setup', '@action-reconnect-account']); await Util.sleep(1); - expect(await gmailPage.read('@webmail-notification')).to.contain('Please reconnect FlowCrypt to your Gmail Account.'); + await gmailPage.waitForContent('@webmail-notification-setup', 'Please reconnect FlowCrypt to your Gmail Account.'); const oauthPopup = await browser.newPageTriggeredBy(t, () => gmailPage.waitAndClick('@action-reconnect-account')); await OauthPageRecipe.google(t, oauthPopup, acct, 'approve'); - await gmailPage.waitAll(['@webmail-notification']); + await gmailPage.waitAll(['@webmail-notification-setup']); await Util.sleep(1); - expect(await gmailPage.read('@webmail-notification')).to.contain('Connected successfully. You may need to reload the tab.'); + await gmailPage.waitForContent('@webmail-notification-setup', 'Connected successfully. You may need to reload the tab.'); // reload and test that it has no more notifications await gmailPage.page.reload(); await gmailPage.waitAndClick('@action-secure-compose'); await Util.sleep(1); - await gmailPage.notPresent(['@webmail-notification']); + await gmailPage.notPresent(['@webmail-notification-setup']); })); ava.default('mail.google.com - setup prompt notif + hides when close clicked + reappears + setup link opens settings', testWithBrowser(undefined, async (t, browser) => { const acct = 'flowcrypt.compatibility@gmail.com'; const gmailPage = await openMockGmailPage(t, browser, acct, false); - await gmailPage.waitAll(['@webmail-notification', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); + await gmailPage.waitAll(['@webmail-notification-setup', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); await gmailPage.waitAndClick('@notification-setup-action-close', { confirmGone: true }); await gmailPage.page.reload(); - await gmailPage.waitAll(['@webmail-notification', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); + await gmailPage.waitAll(['@webmail-notification-setup', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); const newSettingsPage = await browser.newPageTriggeredBy(t, () => gmailPage.waitAndClick('@notification-setup-action-open-settings')); await newSettingsPage.waitAll('@action-connect-to-gmail'); })); @@ -490,10 +431,113 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== ava.default('mail.google.com - setup prompt notification shows up + dismiss hides it + does not reappear if dismissed', testWithBrowser(undefined, async (t, browser) => { const acct = 'flowcrypt.compatibility@gmail.com'; const gmailPage = await openMockGmailPage(t, browser, acct, false); - await gmailPage.waitAll(['@webmail-notification', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); + await gmailPage.waitAll(['@webmail-notification-setup', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); await gmailPage.waitAndClick('@notification-setup-action-dismiss', { confirmGone: true }); await gmailPage.page.reload(); - await gmailPage.notPresent(['@webmail-notification', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); + await gmailPage.notPresent(['@webmail-notification-setup', '@notification-setup-action-open-settings', '@notification-setup-action-dismiss', '@notification-setup-action-close']); + })); + + ava.default('setup - test adding missing self-signature key issue', testWithBrowser('compatibility', async (t, browser) => { + const acctEmail = 'flowcrypt.compatibility@gmail.com'; + const settingsPage = await browser.newPage(t, TestUrls.extensionSettings(acctEmail)); + await SettingsPageRecipe.toggleScreen(settingsPage, 'additional'); + const addKeyPopup = await SettingsPageRecipe.awaitNewPageFrame(settingsPage, '@action-open-add-key-page', ['add_key.htm']); + await addKeyPopup.waitAndClick('@source-paste'); + const key = Config.key('missing.self.signatures'); + await addKeyPopup.waitAndType('@input-armored-key', key?.armored ?? ''); + await addKeyPopup.waitAndType('#input_passphrase', key?.passphrase ?? ''); + await addKeyPopup.waitAndClick('.action_add_private_key', { delay: 1 }); + await addKeyPopup.waitAll('@input-compatibility-fix-expire-years', { timeout: 30 }); + await addKeyPopup.selectOption('@input-compatibility-fix-expire-years', '1'); + await addKeyPopup.waitAndClick('@action-fix-and-import-key'); + await Util.sleep(1); + const myKeyFrame = await SettingsPageRecipe.awaitNewPageFrame(settingsPage, `@action-show-key-2`, ['my_key.htm', 'placement=settings']); + await Util.sleep(1); + const curDate = new Date(), year = curDate.getFullYear(), month = curDate.getMonth(), date = curDate.getDate(); + const expirationDate = new Date(year + 1, month, date); + // Had to add this because if test runs at 23:59:59 it might cause assertion error + // https://github.com/FlowCrypt/flowcrypt-browser/pull/4796#discussion_r1025150001 + const oneDayBeforeExpirationDate = new Date(year + 1, month, date - 1); + const expiration = Str.datetimeToDate(Str.fromDate(expirationDate)); + const oneDayBeforeExpiration = Str.datetimeToDate(Str.fromDate(oneDayBeforeExpirationDate)); + expect(await myKeyFrame.read('@content-key-expiration')).to.be.oneOf([expiration, oneDayBeforeExpiration]); + })); + + ava.default('setup [not using key manager] - notify users when their keys expire soon', testWithBrowser(undefined, async (t, browser) => { + const acctEmail = 'flowcrypt.notify.expiring.keys@gmail.com'; + const passphrase = '1234'; + const warningMsg = 'Your keys are expiring in 18 days. Please import a newer set of keys to use.'; + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acctEmail); + // Generate key that expires in 20 days + const key = await opgp.generateKey({ + curve: 'curve25519', + userIds: [{ email: acctEmail }], + keyExpirationTime: 20 * 24 * 60 * 60, + passphrase, + date: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000) + }); + // Setup with above key + await SetupPageRecipe.manualEnter(settingsPage, 'unused', { + submitPubkey: false, + usedPgpBefore: false, + key: { + title: '?', + armored: key.privateKeyArmored, + passphrase, + longid: '0000000000000000' // dummy -- not needed + } + }, { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); + const gmailPage = await openMockGmailPage(t, browser, acctEmail); + // Check if notification presents + await gmailPage.waitForContent('@webmail-notification-notify_expiring_keys', warningMsg); + // Add updated key that expires in 100 days + await SettingsPageRecipe.toggleScreen(settingsPage, 'additional'); + const addKeyPopup = await SettingsPageRecipe.awaitNewPageFrame(settingsPage, '@action-open-add-key-page', ['add_key.htm']); + await addKeyPopup.waitAndClick('@source-paste'); + const updatedKey = await opgp.generateKey({ + curve: 'curve25519', + userIds: [{ email: acctEmail }, { email: 'demo@gmail.com', name: 'Demo user' }], + passphrase, + keyExpirationTime: 100 * 24 * 60 * 60 + }); + await addKeyPopup.waitAndType('@input-armored-key', updatedKey.privateKeyArmored); + await addKeyPopup.waitAndType('#input_passphrase', passphrase); + await addKeyPopup.waitAndClick('.action_add_private_key', { delay: 1 }); + await Util.sleep(1); + await gmailPage.page.reload(); + await gmailPage.notPresent('@webmail-notification-notify_expiring_keys'); + // remove added key and observe warning appears again + await settingsPage.waitAndClick('@action-remove-key-1'); + await gmailPage.page.reload(); + await Util.sleep(1); + await gmailPage.waitForContent('@webmail-notification-notify_expiring_keys', warningMsg); + })); + + ava.default('setup [using key manager] - notify users when their keys expire soon', testWithBrowser(undefined, async (t, browser) => { + const acctEmail = 'flowcrypt.notify.expiring.keys.updating.key@key-manager-autogen.flowcrypt.test'; + const key = await opgp.generateKey({ + curve: 'curve25519', + userIds: [{ email: acctEmail }], + keyExpirationTime: 20 * 24 * 60 * 60, + date: new Date(Date.now() - 2 * 24 * 60 * 60 * 1000) + }); + MOCK_KM_KEYS[acctEmail] = { response: { privateKeys: [{ decryptedPrivateKey: key.privateKeyArmored }] } }; + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acctEmail); + await SetupPageRecipe.autoSetupWithEKM(settingsPage); + const gmailPage = await openMockGmailPage(t, browser, acctEmail); + // Check if notification presents + const warningMsg = 'Your local keys expire in 18 days.\nTo receive the latest keys, please ensure that you can connect to your corporate network either through VPN or in person and reload Gmail.\nIf this notification still shows after that, please contact your Help Desk.'; + await gmailPage.waitForContent('@webmail-notification-notify_expiring_keys', warningMsg); + // Check if warning message still presents when EKM returns error + MOCK_KM_KEYS[acctEmail] = { badRequestError: 'RequestTimeout' }; + await gmailPage.page.reload(); + await Util.sleep(1); + await gmailPage.waitForContent('@webmail-notification-notify_expiring_keys', warningMsg); + MOCK_KM_KEYS[acctEmail] = { response: { privateKeys: [{ decryptedPrivateKey: key.privateKeyArmored }, { decryptedPrivateKey: testConstants.notifyExpiringKeys }] } }; + await gmailPage.page.reload(); + await PageRecipe.waitForToastToAppearAndDisappear(gmailPage, 'Account keys updated'); + await gmailPage.page.reload(); + await gmailPage.notPresent('@webmail-notification-setup'); })); ava.default.todo('setup - recover with a pass phrase - 1pp1 then wrong, then skip'); @@ -705,7 +749,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== ava.default('get.updating.key@key-manager-choose-passphrase-forbid-storing.flowcrypt.test - automatic update of key found on key manager', testWithBrowser(undefined, async (t, browser) => { const acct = 'get.updating.key@key-manager-choose-passphrase-forbid-storing.flowcrypt.test'; - MOCK_KM_UPDATING_KEY[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; + MOCK_KM_KEYS[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); const passphrase = 'long enough to suit requirements'; await SetupPageRecipe.autoSetupWithEKM(settingsPage, { @@ -726,7 +770,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== await gmailPage.close(); // 2. EKM returns a newer version of the existing key const someOlderVersion = await updateAndArmorKey(set2[0]); - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [{ decryptedPrivateKey: someOlderVersion }] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [{ decryptedPrivateKey: someOlderVersion }] }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await PageRecipe.waitForToastToAppearAndDisappear(gmailPage, 'Account keys updated'); const set3 = await retrieveAndCheckKeys(settingsPage, acct, 1); @@ -748,7 +792,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(set5[0].lastModified).to.equal(set4[0].lastModified); // no update await gmailPage.close(); // 5. EKM returns a newer version of the existing key, canceling passphrase prompt, no update - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [{ decryptedPrivateKey: await updateAndArmorKey(set5[0]) }] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [{ decryptedPrivateKey: await updateAndArmorKey(set5[0]) }] }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await gmailPage.waitAll('@dialog-passphrase'); await ComposePageRecipe.cancelPassphraseDialog(gmailPage, 'keyboard'); @@ -771,7 +815,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(set7[0].lastModified!).to.be.greaterThan(set6[0].lastModified!); // an update happened await gmailPage.close(); // 7. EKM returns an older version of the existing key, no toast, no update - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [{ decryptedPrivateKey: someOlderVersion }] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [{ decryptedPrivateKey: someOlderVersion }] }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await PageRecipe.noToastAppears(gmailPage); await gmailPage.notPresent('@dialog-passphrase'); @@ -779,7 +823,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(set8[0].lastModified).to.equal(set7[0].lastModified); // no update await gmailPage.close(); // 8. EKM returns an older version of the existing key, and a new key, toast, new key gets added encrypted with the same passphrase - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [{ decryptedPrivateKey: someOlderVersion }, { decryptedPrivateKey: testConstants.existingPrv }] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [{ decryptedPrivateKey: someOlderVersion }, { decryptedPrivateKey: testConstants.existingPrv }] }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await PageRecipe.waitForToastToAppearAndDisappear(gmailPage, 'Account keys updated'); await gmailPage.notPresent('@dialog-passphrase'); @@ -791,7 +835,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(mainKey9[0].lastModified).to.equal(set8[0].lastModified); // no update await gmailPage.close(); // 9. EKM returns a newer version of one key, fully omitting the other one, a toast, an update and removal - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [{ decryptedPrivateKey: await updateAndArmorKey(mainKey9[0]) }] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [{ decryptedPrivateKey: await updateAndArmorKey(mainKey9[0]) }] }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await PageRecipe.waitForToastToAppearAndDisappear(gmailPage, 'Account keys updated'); await gmailPage.notPresent('@dialog-passphrase'); @@ -803,7 +847,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== // 10. Forget the passphrase, EKM returns a third key, we enter a passphrase that doesn't match any of the existing keys, no update await InboxPageRecipe.finishSessionOnInboxPage(gmailPage); await gmailPage.close(); - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [{ decryptedPrivateKey: testConstants.unprotectedPrvKey }] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [{ decryptedPrivateKey: testConstants.unprotectedPrvKey }] }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await gmailPage.waitAll('@dialog-passphrase'); { @@ -838,7 +882,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== // 12. Forget the passphrase, EKM sends a broken key, no passphrase dialog, no updates await InboxPageRecipe.finishSessionOnInboxPage(gmailPage); await gmailPage.close(); - MOCK_KM_UPDATING_KEY[acct].response.privateKeys = [ + MOCK_KM_KEYS[acct].response.privateKeys = [ { decryptedPrivateKey: await updateAndArmorKey(set2[0]) }, // update the main key // only include a half of another armored key { decryptedPrivateKey: testConstants.unprotectedPrvKey.substring(0, testConstants.unprotectedPrvKey.length / 2) } @@ -854,7 +898,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(mainKey13[0].lastModified).to.equal(mainKey12[0].lastModified); // no update await gmailPage.close(); // 13. EKM down, no toast, no passphrase dialog, no updates - MOCK_KM_UPDATING_KEY[acct] = { badRequestError: 'RequestTimeout' }; + MOCK_KM_KEYS[acct] = { badRequestError: 'RequestTimeout' }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await PageRecipe.noToastAppears(gmailPage); await gmailPage.notPresent('@dialog-passphrase'); @@ -869,7 +913,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== ava.default('put.updating.key@key-manager-choose-passphrase-forbid-storing.flowcrypt.test - updates of key found on key manager via setup page (with passphrase)', testWithBrowser(undefined, async (t, browser) => { const acct = 'put.updating.key@key-manager-choose-passphrase-forbid-storing.flowcrypt.test'; - MOCK_KM_UPDATING_KEY[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; + MOCK_KM_KEYS[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); const passphrase = 'long enough to suit requirements'; await SetupPageRecipe.autoSetupWithEKM(settingsPage, { @@ -879,7 +923,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== const extraAuthHeaders = { Authorization: `Bearer ${accessToken}` }; const set1 = await retrieveAndCheckKeys(settingsPage, acct, 1); // 1. EKM returns the empty set, forcing to auto-generate - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [] }; let gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); // The new settingsPage is loaded in place of the existing settings tab (this is by design) // However, after a second the newly-activated (old) settings tab loses focus in favour of the gmailPage, why is that? @@ -898,7 +942,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(set2[0].id).to.not.equal(set1[0].id); // entirely new key was generated // 2. Adding a new key from the key manager when there is none in the storage // First, erase the keys by supplying an empty set from mock EKM - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [] }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await PageRecipe.noToastAppears(gmailPage); await gmailPage.notPresent('@dialog-passphrase'); @@ -907,7 +951,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(await getPassphrase(settingsPage, acct, KeyUtil.getPrimaryLongid(set2[0]))).to.be.an.undefined; // the passphrase for the old key was deleted await settingsPage.close(); // Secondly, configure mock EKM to return a key and re-load the gmail page - MOCK_KM_UPDATING_KEY[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; + MOCK_KM_KEYS[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; gmailPage = await browser.newPage(t, undefined, undefined, extraAuthHeaders); const newSettingsPage = await browser.newPageTriggeredBy(t, () => gmailPage.goto(TestUrls.mockGmailUrl())); await SetupPageRecipe.autoSetupWithEKM(newSettingsPage, { @@ -923,13 +967,13 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== ava.default('get.updating.key@key-manager-autoimport-no-prv-create.flowcrypt.test - updates of key found on key manager when NO_PRV_CREATE', testWithBrowser(undefined, async (t, browser) => { const acct = 'get.updating.key@key-manager-autoimport-no-prv-create.flowcrypt.test'; - MOCK_KM_UPDATING_KEY[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; + MOCK_KM_KEYS[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.autoSetupWithEKM(settingsPage); const accessToken = await BrowserRecipe.getGoogleAccessToken(settingsPage, acct); const extraAuthHeaders = { Authorization: `Bearer ${accessToken}` }; const set1 = await retrieveAndCheckKeys(settingsPage, acct, 1); - MOCK_KM_UPDATING_KEY[acct].response = { privateKeys: [] }; + MOCK_KM_KEYS[acct].response = { privateKeys: [] }; // 1. EKM returns the empty set, auto-generation is not allowed, hence the error modal let gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await gmailPage.waitAndRespondToModal('error', 'confirm', 'Keys for your account were not set up yet - please ask your systems administrator'); await PageRecipe.noToastAppears(gmailPage); @@ -938,7 +982,7 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== expect(await getPassphrase(settingsPage, acct, KeyUtil.getPrimaryLongid(set1[0]))).to.be.an.undefined; // the passphrase for the old key was deleted await settingsPage.close(); // 2. Adding a new key from the key manager when there is none in the storage - MOCK_KM_UPDATING_KEY[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; + MOCK_KM_KEYS[acct] = { response: { privateKeys: [{ decryptedPrivateKey: testConstants.updatingPrv }] } }; gmailPage = await browser.newPage(t, TestUrls.mockGmailUrl(), undefined, extraAuthHeaders); await PageRecipe.waitForToastToAppearAndDisappear(gmailPage, 'Account keys updated'); await gmailPage.close(); @@ -948,6 +992,19 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== await dbPage.close(); })); + ava.default('user@custom-sks.flowcrypt.test - Respect custom key server url', testWithBrowser(undefined, async (t, browser) => { + const acct = 'user@custom-sks.flowcrypt.test'; + const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); + await SetupPageRecipe.autoSetupWithEKM(settingsPage); + const composePage = await ComposePageRecipe.openStandalone(t, browser, acct); + await ComposePageRecipe.fillMsg(composePage, { to: 'test@custom-sks.flowcrypt.test' }, 'Respect custom key server url'); + await composePage.waitForContent('.email_address.has_pgp', 'test@custom-sks.flowcrypt.test'); + await composePage.close(); + await SettingsPageRecipe.toggleScreen(settingsPage, 'additional'); + const contactsFrame = await SettingsPageRecipe.awaitNewPageFrame(settingsPage, '@action-open-contacts-page', ['contacts.htm', 'placement=settings']); + await contactsFrame.waitForContent('@custom-key-server-description', 'using custom SKS pubkeyserver: https://localhost:8001'); + })); + ava.default.todo('DEFAULT_REMEMBER_PASS_PHRASE with auto-generation when all keys are removed by EKM'); // should we re-use the known passphrase or delete it from the storage in this scenario? @@ -955,16 +1012,14 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== const acctEmail = 'user@no-flags-client-configuration.flowcrypt.test'; const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acctEmail); await Util.sleep(1); - await settingsPage.waitForContent('@container-err-title', 'FlowCrypt encountered an error with unknown cause.'); - await settingsPage.waitForContent('@container-err-text', 'Error: Missing client configuration flags.'); + await settingsPage.waitAndRespondToModal('error', 'confirm', 'Missing client configuration flags.'); })); ava.default('null-setting@null-client-configuration.flowcrypt.test - should not show error when no setting is present', testWithBrowser(undefined, async (t, browser) => { const acctEmail = 'null-setting@null-client-configuration.flowcrypt.test'; const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acctEmail); await Util.sleep(1); - await settingsPage.notPresent('@container-err-title'); - await settingsPage.notPresent('@container-err-text',); + await settingsPage.notPresent('@container-error-modal-text'); })); ava.default('get.key@key-manager-choose-passphrase.flowcrypt.test - passphrase chosen by user with key found on key manager', testWithBrowser(undefined, async (t, browser) => { @@ -1163,18 +1218,13 @@ AN8G3r5Htj8olot+jm9mIa5XLXWzMNUZgg== }) ); - /** - * You need the following lines in /etc/hosts: - * 127.0.0.1 standardsubdomainfes.test - * 127.0.0.1 fes.standardsubdomainfes.test - */ - ava.default('user@standardsubdomainfes.test:8001 - uses FES on standard domain', testWithBrowser(undefined, async (t, browser) => { - const acct = 'user@standardsubdomainfes.test:8001'; // added port to trick extension into calling the mock + ava.default('user@standardsubdomainfes.localhost:8001 - uses FES on standard domain', testWithBrowser(undefined, async (t, browser) => { + const acct = 'user@standardsubdomainfes.localhost:8001'; // added port to trick extension into calling the mock const settingsPage = await BrowserRecipe.openSettingsLoginApprove(t, browser, acct); await SetupPageRecipe.manualEnter(settingsPage, 'flowcrypt.test.key.used.pgp', { submitPubkey: false, usedPgpBefore: false }, { isSavePassphraseChecked: false, isSavePassphraseHidden: false }); const debugFrame = await SettingsPageRecipe.awaitNewPageFrame(settingsPage, '@action-show-local-store-contents', ['debug_api.htm']); - await debugFrame.waitForContent('@container-pre', 'fes.standardsubdomainfes.test:8001'); // FES url on standard subdomain + await debugFrame.waitForContent('@container-pre', 'fes.standardsubdomainfes.localhost:8001'); // FES url on standard subdomain await debugFrame.waitForContent('@container-pre', 'got.this@fromstandardfes.com'); // org rules from FES })); diff --git a/test/source/tests/tooling/browser-recipe.ts b/test/source/tests/tooling/browser-recipe.ts index 9d917ba46c3..498aaaa4353 100644 --- a/test/source/tests/tooling/browser-recipe.ts +++ b/test/source/tests/tooling/browser-recipe.ts @@ -12,6 +12,7 @@ import { testVariant } from '../../test'; import { testConstants } from './consts'; import { PageRecipe } from '../page-recipe/abstract-page-recipe'; import { InMemoryStoreKeys } from '../../core/const'; +import { GmailPageRecipe } from '../page-recipe/gmail-page-recipe'; export class BrowserRecipe { public static oldAndNewComposeButtonSelectors = ['div.z0[class*="_destroyable"]', '.new_secure_compose_window_button']; @@ -25,7 +26,7 @@ export class BrowserRecipe { }; public static openSettingsLoginApprove = async (t: AvaContext, browser: BrowserHandle, acctEmail: string) => { - const settingsPage = await browser.newPage(t, TestUrls.extensionSettings()); + const settingsPage = await browser.newPage(t, TestUrls.extensionSettings(acctEmail)); const oauthPopup = await browser.newPageTriggeredBy(t, () => settingsPage.waitAndClick('@action-connect-to-gmail')); await OauthPageRecipe.google(t, oauthPopup, acctEmail, 'approve'); return settingsPage; @@ -52,6 +53,7 @@ export class BrowserRecipe { public static openGmailPageAndVerifyComposeBtnPresent = async (t: AvaContext, browser: BrowserHandle, googleLoginIndex = 0) => { const gmailPage = await BrowserRecipe.openGmailPage(t, browser, googleLoginIndex); await gmailPage.waitAll('@action-secure-compose'); + await GmailPageRecipe.closeInitialSetupNotif(gmailPage); return gmailPage; }; diff --git a/test/source/tests/tooling/consts.ts b/test/source/tests/tooling/consts.ts index d5aa11dd217..f53411145cb 100644 --- a/test/source/tests/tooling/consts.ts +++ b/test/source/tests/tooling/consts.ts @@ -1563,6 +1563,29 @@ w8JDG8JLl5x7Zqr4jNVIEKO8q8kBfpzb98PneNgiXvLRQ1hzPxHpeI6uLRlh UcAkCl5YMLyolhq+leCxbC3tOqaTwXs5OpAyC8zblS2cWJePJ1cg+55sN3WF pZv4BU4v7sR2XCRhNbbP5N14NWXYZEADYBpI743KIwA8SXS6BhRong== =3FoO +-----END PGP PRIVATE KEY BLOCK-----`, + notifyExpiringKeys: // flowcrypt.notify.expiring.keys.updating.key@key-manager-autogen.flowcrypt.test + `-----BEGIN PGP PRIVATE KEY BLOCK----- +Version: FlowCrypt Email Encryption 8.3.9 +Comment: Seamlessly send and receive encrypted email + +xVgEY2oQxxYJKwYBBAHaRw8BAQdAej0aNrIghxZcwakib1KGgpJ/i7wxC2B7 +AQQM3cQeL9AAAP9WFmTCICvICQ4MVOUjqPqylHA/YLgrWvf+WWr+IXNwUw9k +zVA8Zmxvd2NyeXB0Lm5vdGlmeS5leHBpcmluZy5rZXlzLnVwZGF0aW5nLmtl +eUBrZXktbWFuYWdlci1hdXRvZ2VuLmZsb3djcnlwdC50ZXN0PsKPBBAWCgAg +BQJjahDHBgsJBwgDAgQVCAoCBBYCAQACGQECGwMCHgEAIQkQ+QwjyQqjcxsW +IQSZR+L74CwkLqXgDr75DCPJCqNzG/KpAP4uBYKz0S22KupCuxjaRQimY72G +DayJXbTfwzlyGaVrzgD8Dbx8p0lnGB+4+F2O0E5aAVLsoDYSpv35G7sao+J0 +EgPNGkRlbW8gdXNlciA8ZGVtb0BnbWFpbC5jb20+wowEEBYKAB0FAmNqEMcG +CwkHCAMCBBUICgIEFgIBAAIbAwIeAQAhCRD5DCPJCqNzGxYhBJlH4vvgLCQu +peAOvvkMI8kKo3MbpgAA/2Zvkl3cQKsLcxEZ+gtwQnkj5oK8q2MiFd5tuvbP +olRpAQDhZZfmBqKD+rm/DD0jJ66hSCLoZOr90FqtduDp1WyKDcddBGNqEMcS +CisGAQQBl1UBBQEBB0DFBVERdNff2bUe8NaitXmxBVh264ZmoREgRMNlxeJ5 +SgMBCAcAAP9liJAf0C+WCq0HxWKniN1HgfYxsh8cYQ58AvtLR820GA8QwngE +GBYIAAkFAmNqEMcCGwwAIQkQ+QwjyQqjcxsWIQSZR+L74CwkLqXgDr75DCPJ +CqNzG4xUAPwNFAUyCFHUemZPJVs5Ewc99/4Pefw2iNWxjWDSJ25OugD/UVIk +CwcIM4OQ7yLvC4CDHgZgstGhNJM4HAukXQr49wo= +=SjBC -----END PGP PRIVATE KEY BLOCK-----` }; @@ -1684,6 +1707,12 @@ eB0Dxje8Q1w52uHm4BYZgwD/fFYiASnRQzCHwTpwyk110W+jPt+rNZ6OIRZH "passphrase": "all org restrictions enabled", "armored": "-----BEGIN PGP PRIVATE KEY BLOCK-----\nVersion: FlowCrypt 7.0.8 Gmail Encryption\nComment: Seamlessly send and receive encrypted email\n\nxcaGBF3H8N8BEADHCZdJnXkMn+asjH6eodMCqIcd4LJdLtOZljWEBsErV2vf\n+zZlsle3lzo/LK5uGThwg0kY7QRFhLS2QII8atYw0E/+WWuWNhm+9UpmuZXp\nLcfkxKErP+xXJaX8ktSt0KXj0RUdFrkgh5EeqG2sdrflFjp90z9XQzt3BEpf\nr+9IhIQZDe239Xu0Zde4VzrxuA2WFHnsZiqTF41CGQmFapcroAZ5JmP+zXLr\nig62LFxkACJq4FxbWX6k1JrFjD+QXs78hB4sVdULDnxI4J8rXvryzW2PMXGr\n9GFAEmIx7ksOHp51HyyqBr8Zcj/YCmwDvm+9GaNKY1oVBeviphBHNXOBYLYO\n9py/1OSS9lCW7Ja0YsrPp+whVC+pDk0NmY0HSZPOXwkJBL8VkcYI4ZMFSLqq\nMpQFN86y61uezUD2gdX+zbEoS+PndIV5lZUuLEO0trRjg1b1/PJSyi8bBgco\nduhEUtGD+n5J3NGyOP9w1jHWaDhjMJ6np/Su8RUC7p4/uXzBxn77jwkYnO4k\nULuK9K6it5gB8kXgaaQ6tc21p2VyID8fb1nPHi3Jg524NkkqsmRA7Ulmo087\nB+k3yK/rbI7rVAs5sFQI45hqKyVvTITm9MSMBDFk+T7VA2NLuM7QhS7hHxho\nkTzo80HEFdQxbzwFZbzdZf8RdTFDN6exnYRuFwARAQAB/gkDCJlRh8K+/hkj\n4HCvQWzXR/WKsCdlUSniPBr3PKgXxv0fzDcEG9AN99Lnkh8g0Swlxyh/SkrA\niRVOaXjvQtcVE09HyOqE0ERG/QblZ+uKbdWmOBj686lyHcswUcmHUvZ36qMN\nn0A6Zh1CBMalkrpECMYseodFKPA8knjG6nOO7/bM/SAKA4J66f0QVCDlVdkM\n2O3P8U7WnEEma3S8OfNIzG+JkoR36X8g/spCw3Wbh3MRwIQtpwkPbWTBm35a\nJ5avbnt5wJAsfJdQ2uGja67bHgt5SnYNzo4pzvl0p1Fvnp3I8jHMHoCGgdVj\n+CjgdH9tQgVy89KwlS1/4BeoYHkH6XZNv1+5sMJeA2J8NnEXf8kvkhBRwWqe\njLz00m0C15rkDp78zPLc0LHASYxasn2piuALTvfP2V1vXzsxaemtidnS42zd\ncNRuv9KyCgBRApBHVVn/3vAL9LLF+JlP2c1JXGQe2HC8M55GsX8HOKOFzZ/o\nfsJCyR1MzveuEYdXKbunQwk16rwKcNFhl14+xMoVmqRIP+4ufiB/v7LAD8Zn\nhJh0FkhLbG+Ta+ZLQwnyviqnObASCW1tDGtZCFCAfv3SzjaHnNLCBxZ8s+Z1\nKQ2mFyWl3mFPjQOwoQsL6ghwx5Rv/UtWE5MknDyJlp3mq+Q4bW7o9JIV1en4\nyHPV6kMBFmigsIVnEO9sGCmH1ScqWcrrbhF4QUGPi6cqpwMPlKevw9hPdCBO\n2F28M/WAN77te+Q2vDAL7hB206AvUtZBN7ngWpOtwtx+J6M7meKlbKoVxgNw\nayeieRtTfI/9yLLBpQjx7TRZiozT+yrlRSHPvfxOBsTi5/Z7KXkzMOf4Ow7q\nxJ+/oJPeWfgUTUQaCzKnRspaJalUpprW6DsAbQxDpO5QHKLlF4yIE4X7Tio/\nF2xtf9F8E6IdpvZJemxIUGBMD9pOVNOIHTsZwFKXiHG54fTeI9nszPYxOLp9\nadYFHgXIn+PS5omY7O6MNnj6zM1i8tFJdW282MGTpCIIorlZ3IV0bm+eVvdS\nJCEYDAwDWGxNTnKmr4QIbM6UMUK4xY0AtE++269sm0V5PF3+n7aj64ERihsR\nJUIZK9w2nmelBy2XPWTz9CI8k6+E2KjW47RfWRaz074l9zlqGLQQ/Ixp2Uhh\nUPcdHwrL8knUdS1xpzu2KiifdttQUfE+eWos3lfwZrYqscexkNA/2RVmbcnM\nv+I8Dvz8khSOUPAOA/xMUetGFclmlWZb3iyJu6jpemw08K7twl5luMytMdkR\n8gKru40fk+uF14IWszzeuQl7bqlqxg0BFGaEuYQsbfqbZm7uUomgFD619Tn3\n4VB3waKk4IWjQU3+0Lkl/+y/95v/3kXoQX1BwUgfBvcpUn2R9NJ1Ajfimwnr\nMuET4pxanwvrpjbE0PP1Xzsq6FNlVKXZhGDLS66lau5c7ZddgJ8H5PxjfjJD\nV9PkpYHdCfNgss2gCIrycxssQSfHlQvlPXDm6h6E67gfYKGq+jCyTh2EOHTx\nJnedrf5lIeSSsgmMe+P20tb56nDF3lUCONANIUw9VgNZH2x9azRMMc2ewJqi\ncLYg1gRQImmOH1Ru8YcOTL7DjhU3HdxOUfuEaJSO3gBRnCISgbkcYbXx1C/9\n/yWNo0e61ZLkiYJtiin6TjGJ5+FeIfNxhJhWTCgqonSGMtr3Sx+dF+3ey470\nRmlOBD1+D5OA6BBMSzgGySnu3DRLwHRR7ol6FTB+GX0TXiYNo7xSnVLUpn5D\nt86iaFW9xM/9oBKhgldV3rl/Q6zNM0hhcyBQdWIgVGVzdCA8aGFzLnB1YkBv\ncmctcnVsZXMtdGVzdC5mbG93Y3J5cHQuY29tPsLBdQQQAQgAHwUCXcfw3wYL\nCQcIAwIEFQgKAgMWAgECGQECGwMCHgEACgkQvGFPcGjbbiOvNQ//QJ+7uCnL\nM/3LS0BAVM5l+xGs+jHBBne6J7TTd3BQmzz5wGdZWDq3eIq8NsDG5uborW9L\n469tp4TwNRrq4/vIhDNuWBoWDWsWXm4jE5FoHCps4WUGZud2SRaIdQRHgYbZ\nrXwoLg8sTbbGYlGYB3sclBsCz8l9Dl3iwQIV4WMwYyftEZ41YnwKeh5UVBte\nYqO1C/OaabNqUNWbcnJS7UyMMjfLae6ETsRJMQuPwFPgscek4innGEPpidjn\noCU57ubORj/08odyT9DCG6P7rbbKFUNJQVi2fqedIKRSj1vZifXw2mY25VkR\nm6cbpl5wn2nFrjzZvOrhovpAQUFtD5Rt/zYuHsivyD7O68Z8gKrqb2tc0xd3\nzRSuaIjW8hGO50EnRzm/+i+Aun4k8RKcVV6RRW1aNCtwQad1CtFWY9exoI6r\nDuCcqrPGz7ZKe1U6nOxIsp+KO1B3DqqlCwvQsaL6UOBgwwWsMtp67NknpvHv\nXiQ36Y+hMuT77pudpa29d2BwShQAVpAVoBatkoNvRTJ9nlkjug8g92FPlnoS\nhAbESh+c6eRQ3veJdjk4Yf5flRSebZ2Ln2Da1+0MCaaZTUynoB52fvEm4abH\n7Jarcgws9ubX7MeFTS3aJeHgP/fdjM93/dAij76jR7Ap/yVgLmIbY59IpAAA\n2bTW8y2Lb+wn8SHHxoYEXcfw3wEQALXMX9tzj5x1JDVM6Gb2T4Gkdlr9OnXv\n1DvMcHkbB8YU1OR14l9/OFdiX97LiMCe98Ci5P6Z2w+4sis/MXSIurJrdJGI\nsFrGxfvuSOsZMiAvb2k8v2/fF9uI7SQcX8zdE9s2pv57WImDG1D9P+O0mYD2\nv4F9qhQ8xEGtuPkPcR6bJdWnUFkZGBoGLyzZq/3sAGXEX1qtLmbWwfUC4SxB\nrzWcBfa+8JUXYx7E5RWIdlU1+h5MRzUHldT1GeWAGcWwMv01QjoLozYBPlms\nYI0h6s0zixRZxZNVutuVIlQCigJszsZfROCbcivnN1WUWj1FEhYkdefmZUw3\neiUPYnKxSfNiS+FKtU07OO3JelOIKMmcbC+Mg9wP1T+6HuTA2tyK0H3pw8TR\nvM6O2Q4/jsExv9J5Gk9w3Hz6BNwHTXu5b3t3Us02MxtfnsObtQgBU5mvRQbC\nUMAeVryZLiJULfO3KGacXyjdPV8sooAU2JXu3NhJgHCQxZVMgVz6JYairaFT\n/KxhqWl/VkuypOYQo1pIbxBe+JY5O5JrwiJyIT0Kk/DJG+j3Qgl8HXfE/f2S\nIXqQFIDgu9SKFn7ks33QNglgoC2KVB1ouE7fHcph2Igp+zQ/wCkzji99na6K\nlOjExcJzD+17RjSjru3DOOKlN/AX3hWa4yY2L+ws29mFbC5WahUdABEBAAH+\nCQMI54iGJZEbZAXgYEBltF6CjnuH6epcCZk5FzE2czTnV5YQS92gEsDgzKdS\nLlxFamqxoKDfPEm3cskxEVDB8Lglj7O2TAlUiLJs7Lx3Oi7P4OmWeZajfuf4\nc8+PPvLZtigZadVgwtwlwD+doTfcfaX+u0s2KVd0FQJOLjK+dQIZUAEUaQgM\n0DvoGIG8ZvSjLAMzhn7uF+kY2+kBy1O1tTkYN78C0hl0JTtJ4fnJ3IWbf8v7\nddEL/sXZ8Os3yhX5w8xiSk4a82Jd2PbctsDGhbzVMlWX1LO361Rwwouaov+C\njCYFr+y+JqBvibBErBE9e6OsE0Bz+vlxwfrjCthQlblBNgPXneQWbTA7/g5r\nAErUMp09L3lt32jbhSF4ALC1DClFW5AU9/HpNEHSY5e+FqGiq5OdHe/cM7YX\nXz8YuuqW2Mcfiur1uYFtuFoHl9DCHNbbSQw/7Trr2jb70F14A9QiKn3qhizq\nQ7ztaIjnFIf8uLgD9WBnzOT0eL5rIT9LoHILmHgjt3DsHccA3M/PwoVAqJuf\n74d/MLgMqlSNfv7BFkFglxMUkJbQq56kn8e6ECRKdlQHlDO13W8BPoE11w4f\nCwKC6yeAidgiknHEtsdWZ2t1Eoc8IlFNlz1G75TJd9aPPneUuGXzfGqj8CI6\n6TqbfcmB0Rwl//goM22Nvjwm0X54kN77DhobYV2RLkis1fWuxCdpm0IfV7aF\njKNBZAKg8axFXlG521Zu/r2T/uSzqrJCkU7rApN4KBK0WJHLdzD//gDg7L5Q\n2PF6jw3SIRuiE8ywE3tH1ELPz2cjkjGa4z+LgWysNam3f6TBjaZ0LmRPlPju\n05cVPbq1wfEw4pinx4gzmzdm/1ur552hoNcSZylGwAyHZp2mRS4oXEWRO+Cz\nfU0whDqDaaeyNtIAIzMb2C1azVvt+Rs1SLvtjzTDLffMsRfmk8NhgOfPgA9W\nqCjhZBWhwrjzZFrKl3Y+e2ZpeBBZ80/NdIdWihNlhg0rgSlv6amn6zZTVvxi\nfEts/SD6AW4o8QlCfSRHX/vBZRigHixQeFe+Q5yNGaZoY+QWNN8noq8dVgvo\nSS/PvsQG5HvI8/Ai/bpl1MQU7mVQZSqw5jk+WxOtHs4HbvJWGXZZMXCQhpL5\nqOBnhYULK69orMsBklY5xB04vpAAtc7UbCfHCcovCL/a8kZLgr33P0+OCB5L\niAdQQwr8yl/az8Px1MN1p3U0v9uEDXeG1wYy0PwwK7b4np3A+UePvc/4ucvi\nL6wPnZJBr3n8CtE6LIpN8QMJPlSurX32zyljl4VVSuV/cCI5SwiPO6bc3bwZ\nNsHb+2ozCPvmXeBthOMRAZEWGPosUyt0DZKrB+jquc9ckxaipKbqTCHVn8gl\nm/XZxcAlWGX4CSML09qznJQuPsW6pf/34ewFIRX9YtZhfUbvptG5V1bTjybO\nSeGwcuXzrWGS+RSRSaqBuLAHutORZxllxA/1GJ0uNDRtHPWTC/t97N+VgavM\nQJOvd6qAIF/EpadIO3E1Q91sVFlzBY/WlZrUVOZc+8iAgMTLSIKLU2L3Co6A\nC8XnEjg8pOQssuttdeyj+u9GN0W5KVIcqkBvzEW0jnETgrLTTSM983tf/TEb\nXRoPyQKHNMfZKB7wbi6JsFf2AqP0eyFBKF6z0d2NojLtfhiQj1y1/3/7sFy4\nGjN7uEMaugx4QT3gKLKVYJAacSODDA3C0ujIHRd42USdZHzPwqIHt2nzmUlh\nl2YG1GI14aSDhV7+R9QEh+Bm0LftXkkLirHEz1ySkcLBXwQYAQgACQUCXcfw\n3wIbDAAKCRC8YU9waNtuI5lZD/9LLPVKkRYmWiaeZEiP/BcnFKDf2RTEPJ0J\nfwgu2YlIwNhrzbUbvd6w4u5+I6sAwhfFglDLuXa01gxos8JYtkBSyitJ07Q5\n+yVnm5TMA830AYFdH5KAZFJbBIHEvPhC7D0xBjMmPCBDXggabqNhWetZFUV2\nL27sSzKurFRSHmHoumX4Rna0erKM69HneQCNO3qUbEJNU7sTELlRuVuC//4V\nBVyUE6WlmYZGlnGMdg1n0n4YJ8YVOU0HlAhpTizznMtkbGPTfDM/WC4vq8bF\nyPUwAthEE8oW2cilhN5/E2HvsJCEBZJ9LR05mNnJq5nJE0LP1j1V7zk0DsJB\n8WlLo349NdOYc4gW803kFucSF9GBRczhCRiZ5tL9l7WqglKvnFc2D/fDVP3z\nnA0ghurul+lMmQG4rwoHTbSpAiUunNGw5i8Z8Man617SQR2//0XCjiLdvVC8\nAw6YO6W8ixz1Xh6MMMALH1aQv+RmKBuW3mpxaLubLB6+slMBKP7Eo4/BYiyE\nj29jHsFpekkRKakH987mmmx6Ff7du6N9PNisYBR+88iepzgqz+AE/J5j0ruL\nsqWCGp5p/e/kXYyHiYzf1Q7d6TWqhasKfYzAmBJ43zNlUmBUyha0GJvc2FEy\nCp0MTbVgvGfOVCs9njJPyDbpM1nu4HL0Gm6mnf2YIS0k86Pynw==\n=Njkn\n-----END PGP PRIVATE KEY BLOCK-----", /// "longid": null // tslint:disable-line:no-null-keyword + }, + { + "title": "uid.less.key", + "passphrase": "correct horse battery staple", + "armored": "-----BEGIN PGP PRIVATE KEY BLOCK-----\nxcMFBF8/lc8BCACwwWWyNdfZ9Qjz8zc4sFGNfHXITscT7WCMuXgC2BbFwiSD\n52+Z6fIKaaMFP07MOy8g3PsrW8rrM6j9ew4fh6Kr6taD5JtZfWEWxSnmfl8T\nMqbfcGklJZDyqbSlRBHh53ea4fZe/wCiaL2qhME9Pa7M+w/AiCT1LuXUBiKp\noCLVn1PFf760vdsz5CD+kpzBIZ45P6zZxnR/P6zLsKjr5nERlIDZ1gWtctx9\n9ZEEVBrgnEE4dBIT1W/M/XbEwsKn1HGOyTBvzeEfM863uW0V9HKmjilbMF2P\nfJ583t1HzuhA7IewcgX/VGC4QKMnukUpRhJQPlcVFSy0zjD9zQYIh437ABEB\nAAH+CQMIblUClAvPYEvgLJlwFM3vC1LLOtMvegEdpUDVA0rpZLASe9RoyEbB\nPGue+yaxxu06N20fsqIxaBh3+uU2ZVfcEre/5XNCj6QxHzqSbclMyHUyVHlv\n/G308yKMyjvwj3mx1hNY5frDb7Pop4ZSftpx1R3tXU1DC1DGy+3Whp41BKAF\nahSQ5oK2VjUFqdoej6p46vt0pt9JOsX7T2eX7Z7TcPoJPNZ0rBDYJDV4RVYk\ntdgA2P4mfbjHZOquexzRgGY9Pn7X/NciUrbmfA6sxyR21aG0xAXMk91bwPDs\nSEEj7ikpIlt7F87yafzwS4JFPzuhhGpZjK1f6t24fAAmufKCdt+IEV4EgkBI\nQWrfUUAXytHIPFyP3z4gcIitmx10DqArxhHeR0sKjtAjOKrMP0qBiQAG6cH+\ny4CdRiBiuEDTazgePzIDJMgIjmWH/hxl5puoEKkQAR9kiiU0bDtphSAQ5GXw\nc/1WhYacYWJytUM+uUWMFAdryd93YmRew1kYxqdZn5AywzOOAbTWD6Q2GME5\n0o0Adfw4CopT2VxsbRq4X74DPtXnReyFGd0167IV3Y8HToHyM4gJxxMVXF3G\nTNW7CSq2L53kklynLtBnAuJKwunR8my7Sm+CX/errsXpq/u3QGZDeHlAh8ul\nrHeqOTZwEqGHxHb1FcQJ+1QQohrwJp2hHKXxgZyGQH8ykTZyNpPAiqkhcl9O\nDJdxq4Ke6wistyzF/sRGRcaXaLHZ8dKS8TIjjzGuMWMaZtBO+6EqIE5JgEHe\nt+SdnMeEZ9kDtWx2+eTb/j5IFuIPlWjRNndad3qpw17wvLufSUs06Pjd5O7q\n3k38hvPHNpCyWWsLnddnCGJZwH5uXCsfKqrO1JkY+0gJISxQ0ZNvMCki2tpZ\nk3ByPEnFoT4c6f8eJMQhODqC8Do9xrTHwwYEXz+VzwEIAKp98eVpCy1lIu26\nHdR5CYlQ5aVhqOVPlk1gWqwQwBBOykj3t3nJtA2tS/qgSgbNtk1bf7KSPUKI\nE8vBGZ/uHCtC9B19ytZxHI51TQtTJgbOkuRkq7KizB+ZZ1TPwrb4HyDxtw4L\nK6kBA0vhvOZeWh4XD7CPSjN457eCaKjnaD6HuvvTin4EVJ9G6B9Ioi6Oyi98\nPB0JA3dpPY4cx/3eggx18cAPeZwiO7vIy0VHtq/G8Obf2Tzowmz1vsgTm+fV\npiZ8lQlQkNBn5Z9/mayZ4bMA1EGaQGzfzS+r4AYP+/UxXRCMlwZ3lt7YYnKI\n5lIZX73TwXzuMwFqGEevIJzD9YkAEQEAAf4JAwhHFiWWy6b0muDxhFu5N7oX\nlhSfbD+RSvezCU8xpDHbkvoOZRC21bKJ1jmkvbC/KKAlxNz5UYJ/OFtffAok\nf0aTlkrNvPxN9apqDgwvsjzC10//3b9BzHjds2rrpGHKjzyapAVkEl0PGWCR\nVPdfjC/f5t7GMzOsSNmTqHVS+aCX8aA48BKkjDjFOUjpLGSqVPxoMTe0gUpa\nNxgJhIb5RZ+6JjbmWooZ4nw/GroUGYfupRr4TG3TYVVGXCHN+/CEClyhJDCm\nsqc1ZhdarNINGVndzz/i5sBbuNMnph6j6Mh72duseSEiOxYZ0iOrwNosC0NS\nqDHA+jBHyP405U8N6V1EBKf3Z+C3+vqSxiR37JkwWcaXEDoJm4oNSI6yA1aa\n8QJIcUMEapfoCmA0alKzLvng5wLCEC82MvPMezkF1O6vBXCMBJs9lEGg/61K\nwkiIpz2FEdulWe7Hca66KTIHWLcd0X1mF7L7XK25UW7+1CrX0cqMEhXi1wGS\nSbqKIVA5bEbwNo1VgENgF0NnsR7Q8H+94k0lems8vw4xS98ogVqFdGTmGF0t\nijE4yf4M9jt7LYWGfru2DDVIHf+K7L+DuOqcjBVXVIy0x+NDSYBnLgIYujsF\n5tMv33SfE17F/CHJDAujY5yTxuXDdzMmxYahsg6vx/fbXZVwm2RFpxCzI6pV\nE/YWhOFMknNHVpiqvQ91Y7nOJlHQAe9RmsGcxng0bwsE1J277JozUr5PNXA9\nZDPVG7/3nHnUnNwnXupHAsiYW4aN/uFUXg5CoArXvj2SHjWQSBMwWDQK9jC5\nYVzi15D9Jt3xYDXpDbSEf8N+d8C31Jx3QedDi/ei5xs/9CJ+DqbBxRUW04jj\nr8mew9pM2+gpDS5DoNLSBJ1vn3OIRLnCudmSJBHs3NMh85qF07bc1+sAozpZ\nvM7CwF8EGAEIAAkFAl8/lc8CGwwACgkQKBMN0dHENohRNAf/Z5G5pySJe4tk\nG1pGQOLjZms08e1KGQlbRtZR8WN2ySCe3Pyla/R3KQRJBQS6V926GKnvsOZC\n3CWVKHDcn1Rx2uV3GH8VWOHfT+EjQI7zCoQAppVEX4uJ4BCxP5Z9CgSxL8zH\n31AHwLEtCqDfeZf8dttihfafyAUFKCCrN5R6cP2AtUlRDE1XRdTJ8zRk4mRX\n81r0vXC1Xfs1zBy3YnDIJVJcEro9v7yOn/5WBtQT/jnBvJZ/gBieolgXUrRb\nV5PJ0lZPFfMdYjjYR+i7j3+/j59kd1Wuz+6I572J+j4lWlPIvGk2V+rzzHqK\nCciXuhqnLwoVF5/uXMYffVtfl/OU+w==\n=EqcV\n-----END PGP PRIVATE KEY BLOCK-----", + "longid": '123' } ] }; diff --git a/tooling/build-types-and-manifests.ts b/tooling/build-types-and-manifests.ts index b023cf3d227..14965a35aad 100644 --- a/tooling/build-types-and-manifests.ts +++ b/tooling/build-types-and-manifests.ts @@ -72,7 +72,7 @@ const CHROME_CONSUMER = 'chrome-consumer'; const CHROME_ENTERPRISE = 'chrome-enterprise'; const MOCK_HOST: { [buildType: string]: string } = { 'chrome-consumer': 'https://localhost:8001', - 'chrome-enterprise': 'https://google.mock.flowcryptlocal.test:8001', + 'chrome-enterprise': 'https://google.mock.localhost:8001', }; const buildDir = (buildType: string) => `./build/${buildType}`;