Skip to content

Commit

Permalink
Merge branch 'master' into issue-5667-added-message-decryption-on-thu…
Browse files Browse the repository at this point in the history
…nderbird
  • Loading branch information
martgil authored Aug 15, 2024
2 parents 3462ce6 + 748b960 commit 8b44889
Show file tree
Hide file tree
Showing 79 changed files with 935 additions and 1,774 deletions.
3 changes: 2 additions & 1 deletion eslint-local-rules.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,8 @@ module.exports = {
if (propertyName === 'forEach' || propertyName === 'each') {
context.report({ node, message: DO_NOT_USE_EACH });
} else if (propertyName === 'map') {
const ancestors = context.getAncestors();
const sourceCode = context.sourceCode ?? context.getSourceCode();
const ancestors = sourceCode.getAncestors ? sourceCode.getAncestors(node) : context.getAncestors();
const parent = ancestors[ancestors.length - 1];
if (parent && parent.type === 'ExpressionStatement') {
context.report({ node, message: DO_NOT_USE_MAP_EXPR_STMT });
Expand Down
23 changes: 11 additions & 12 deletions eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import noOnlyTestsPlugin from 'eslint-plugin-no-only-tests';
import headerPlugin from 'eslint-plugin-header';
import jsdocPlugin from 'eslint-plugin-jsdoc';
import preferArrowPlugin from 'eslint-plugin-prefer-arrow';
import importPlugin from 'eslint-plugin-import';
import noNullPlugin from 'eslint-plugin-no-null';
import localRulesPlugin from 'eslint-plugin-local-rules';
import eslintConfigPrettier from 'eslint-config-prettier';
Expand All @@ -17,7 +16,6 @@ const commonConfig = {
header: headerPlugin,
jsdoc: jsdocPlugin,
'prefer-arrow': preferArrowPlugin,
import: importPlugin,
'no-null': noNullPlugin,
'local-rules': localRulesPlugin,
},
Expand Down Expand Up @@ -94,7 +92,6 @@ const commonConfig = {
'@typescript-eslint/no-unused-vars': ['error'],
'@typescript-eslint/no-unused-expressions': 'error',
'@typescript-eslint/no-use-before-define': 'off',
'@typescript-eslint/no-var-requires': 'error',
'@typescript-eslint/prefer-for-of': 'error',
'@typescript-eslint/prefer-function-type': 'error',
'@typescript-eslint/prefer-namespace-keyword': 'error',
Expand All @@ -110,7 +107,6 @@ const commonConfig = {
'header/header': ['error', 'block', ' ©️ 2016 - present FlowCrypt a.s. Limitations apply. Contact [email protected] '],
'id-denylist': 'error',
'id-match': 'error',
'import/order': 'off',
'jsdoc/check-alignment': 'off',
'jsdoc/check-indentation': 'off',
'jsdoc/newline-after-description': 'off',
Expand Down Expand Up @@ -154,20 +150,23 @@ const commonConfig = {
radix: 'off',
'require-atomic-updates': 0,
'sort-imports': 'off',
'spaced-comment': [
'error',
'always',
{
markers: ['/'],
},
],
'local-rules/standard-loops': 'error',
},
};

export default [
{
ignores: ['extension/types/**', 'extension/js/common/core/types/**', 'test/source/core/types/**', 'build/**', 'extension/lib/**', 'eslint.config.js'],
ignores: [
'build/**',
'conf/**',
'eslint.config.mjs',
'eslint-local-rules.js',
'extension/lib/**',
'extension/types/**',
'extension/js/common/core/types/**',
'test/source/core/types/**',
'test/source/tests/**/*.js',
],
},
pluginJs.configs.recommended,
...tseslint.configs.strictTypeChecked,
Expand Down
2 changes: 1 addition & 1 deletion extension/chrome/elements/attachment.ts
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,7 @@ export class AttachmentDownloadView extends View {
try {
const googleDriveFileId = url.split('/').pop()?.split('?').shift(); // try and catch any errors below if structure is not as expected
url = googleDriveFileId ? `https://drive.google.com/uc?export=download&id=${googleDriveFileId}` : url; // attempt to get length headers from Google Drive file if available
} catch (e) {
} catch {
// leave url as is
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export class ComposeErrModule extends ViewModule<ComposeView> {
} else if (typeof e === 'object' && e && typeof (e as { stack: string }).stack === 'undefined') {
try {
(e as { stack: string }).stack = `[compose action: ${couldNotDoWhat}]`;
} catch (e) {
} catch {
// no need
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@ export class ComposeQuoteModule extends ViewModule<ComposeView> {
const from = Str.parseEmail(this.messageToReplyOrForward.headers.from || '').email;
const date = new Date(String(this.messageToReplyOrForward.headers.date));
const dateStr = Str.fromDate(date).replace(' ', ' at ');
const rtl = text.match(new RegExp('[' + Str.rtlChars + ']'));
const rtl = new RegExp('[' + Str.rtlChars + ']').exec(text);
const dirAttr = `dir="${rtl ? 'rtl' : 'ltr'}"`;
const escapedText = this.convertLineBreakToBr(Xss.escape(text), method === 'reply');
if (method === 'reply') {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -425,7 +425,7 @@ export class ComposeRenderModule extends ViewModule<ComposeView> {
let normalizedPub: string;
try {
normalizedPub = await keyImportUi.checkPub(textData);
} catch (e) {
} catch {
return; // key is invalid
}
const key = await KeyUtil.parse(normalizedPub);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,7 @@ export class ComposeSendBtnModule extends ViewModule<ComposeView> {
let mimeType;
let data = '';
const parts = src.split(/[:;,]/);
if (parts.length === 4 && parts[0] === 'data' && parts[1].match(/^image\/\w+/) && parts[2] === 'base64') {
if (parts.length === 4 && parts[0] === 'data' && /^image\/\w+/.exec(parts[1]) && parts[2] === 'base64') {
mimeType = parts[1];
data = parts[3];
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ export class ComposeStorageModule extends ViewModule<ComposeView> {
try {
// no valid keys found, query synchronously, then return result
await this.updateLocalPubkeysFromRemote(storedContact?.sortedPubkeys || [], email);
} catch (e) {
} catch {
return PUBKEY_LOOKUP_RESULT_FAIL;
}
// re-query the storage, which is now updated
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,11 +47,9 @@ export class GeneralMailFormatter {
`Could not sign this encrypted message. The sender email ${view.senderModule.getSender()} isn't present in the signing key's user ids`
);
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const msg = await new SignedMsgMailFormatter(view).sendableMsg(newMsgData, signingKey.key);

return {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
senderKi: signingKey.keyInfo,
msgs: [msg],
renderSentMessage: { recipients: msg.recipients, attachments: msg.attachments },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ export class InboxMenuModule extends ViewModule<InboxView> {

private renderFolder = (labelEl: HTMLSpanElement) => {
for (const cls of labelEl.classList) {
const labelId = (cls.match(/^label_([a-zA-Z0-9_]+)$/) || [])[1];
const labelId = (/^label_([a-zA-Z0-9_]+)$/.exec(cls) || [])[1];
if (labelId) {
this.view.redirectToUrl({ acctEmail: this.view.acctEmail, labelId });
return;
Expand Down
3 changes: 0 additions & 3 deletions extension/chrome/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ View.run(
const uncheckedUrlParams = Url.parse(['acctEmail', 'page', 'pageUrlParams', 'advanced', 'addNewAcct']);
this.acctEmail = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'acctEmail');
this.page = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'page');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (this.page && !/^(\/chrome|modules)/.test(this.page)) {
Ui.modal.error('An unexpected value was found for the page parameter').catch((err: unknown) => {
console.log(err);
Expand Down Expand Up @@ -244,7 +243,6 @@ View.run(
$('#status-row #status_local_store').on(
'click',
this.setHandler(async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await Settings.renderSubPage(this.acctEmail, this.tabId, 'modules/debug_api.htm', { which: 'local_store' });
})
);
Expand Down Expand Up @@ -391,7 +389,6 @@ View.run(
);
statusContainer.empty().append(authNeededLink); // xss-direct
$('#status-row #status_flowcrypt').text(`fc:auth`).addClass('bad');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Settings.offerToLoginWithPopupShowModalOnErr(this.acctEmail, () => {
window.location.reload();
});
Expand Down
2 changes: 1 addition & 1 deletion extension/chrome/settings/modules/contacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -233,7 +233,7 @@ View.run(
const pubkey = await KeyUtil.parse(armoredPubkey);
await ContactStore.update(undefined, email, { pubkey, lastUse: Date.now() });
await this.loadAndRenderContactList();
} catch (e) {
} catch {
await Ui.modal.warning('Cannot recognize a valid public key, please try again. ' + Lang.general.contactIfNeedAssistance(!!this.fesUrl));
$('#edit_contact .input_pubkey').val('').trigger('focus');
}
Expand Down
3 changes: 1 addition & 2 deletions extension/chrome/settings/modules/my_key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,7 @@ import { Xss } from '../../../js/common/platform/xss.js';
import { FlowCryptWebsite } from '../../../js/common/api/flowcrypt-website.js';

declare class ClipboardJS {
// eslint-disable-next-line @typescript-eslint/ban-types
public constructor(selector: string, options: {});
public constructor(selector: string, options: unknown);
}

View.run(
Expand Down
4 changes: 2 additions & 2 deletions extension/chrome/settings/setup/setup-key-manager-autogen.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class SetupWithEmailKeyManagerModule {
/* eslint-enable @typescript-eslint/naming-convention */
try {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
const { privateKeys } = await this.view.keyManager!.getPrivateKeys(this.view.idToken!);
const { privateKeys } = await this.view.keyManager!.getPrivateKeys(this.view.acctEmail);
if (privateKeys.length) {
// keys already exist on keyserver, auto-import
try {
Expand Down Expand Up @@ -115,7 +115,7 @@ export class SetupWithEmailKeyManagerModule {
}
const storePrvOnKm = async () => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
await this.view.keyManager!.storePrivateKey(this.view.idToken!, KeyUtil.armor(decryptablePrv));
await this.view.keyManager!.storePrivateKey(this.view.acctEmail, KeyUtil.armor(decryptablePrv));
};
await Settings.retryUntilSuccessful(
storePrvOnKm,
Expand Down
2 changes: 1 addition & 1 deletion extension/chrome/settings/setup/setup-recover-key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ export class SetupRecoverKeyModule {
const backups = await this.view.gmail.fetchKeyBackups();
this.view.fetchedKeyBackups = backups.keyinfos.backups;
this.view.fetchedKeyBackupsUniqueLongids = backups.longids.backups;
} catch (e) {
} catch {
window.location.href = Url.create('modules/add_key.htm', {
acctEmail: this.view.acctEmail,
parentTabId: this.view.parentTabId,
Expand Down
4 changes: 1 addition & 3 deletions extension/chrome/settings/setup/setup-render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,6 @@ export class SetupRenderModule {
public renderInitial = async (): Promise<void> => {
$('.email-address').text(this.view.acctEmail);
$('#button-go-back').css('visibility', 'hidden');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (this.view.storage.email_provider === 'gmail') {
// show alternative account addresses in setup form + save them for later
try {
Expand All @@ -40,7 +39,7 @@ export class SetupRenderModule {
return;
}
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

if (this.view.storage.setup_done && this.view.action !== 'update_from_ekm') {
if (this.view.action !== 'add_key') {
await this.renderSetupDone();
Expand Down Expand Up @@ -142,7 +141,6 @@ 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');
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
} else if (this.view.storage.email_provider === 'gmail') {
try {
const backups = await this.view.gmail.fetchKeyBackups();
Expand Down
17 changes: 4 additions & 13 deletions extension/js/common/api/account-servers/external-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,16 +5,16 @@ import { Api, ProgressCb, ProgressCbs } from '../shared/api.js';
import { AcctStore } from '../../platform/store/acct-store.js';
import { Dict, Str } from '../../core/common.js';
import { ErrorReport } from '../../platform/catch.js';
import { ApiErr, BackendAuthErr } from '../shared/api-error.js';
import { FLAVOR, InMemoryStoreKeys } from '../../core/const.js';
import { ApiErr } from '../shared/api-error.js';
import { FLAVOR } 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 { ClientConfigurationError, ClientConfigurationJson } from '../../client-configuration.js';
import { InMemoryStore } from '../../platform/store/in-memory-store.js';
import { Serializable } from '../../platform/store/abstract-store.js';
import { AuthenticationConfiguration } from '../../authentication-configuration.js';
import { Xss } from '../../platform/xss.js';
import { ConfiguredIdpOAuth } from '../authentication/configured-idp-oauth.js';

// todo - decide which tags to use
type EventTag = 'compose' | 'decrypt' | 'setup' | 'settings' | 'import-pub' | 'import-prv';
Expand Down Expand Up @@ -160,15 +160,6 @@ export class ExternalService extends Api {
});
};

private authHdr = async (): Promise<{ authorization: string }> => {
const idToken = await InMemoryStore.getUntilAvailable(this.acctEmail, InMemoryStoreKeys.ID_TOKEN);
if (idToken) {
return { authorization: `Bearer ${idToken}` };
}
// user will not actually see this message, they'll see a generic login prompt
throw new BackendAuthErr('Missing id token, please re-authenticate');
};

private request = async <RT>(
path: string,
vals?:
Expand All @@ -192,6 +183,6 @@ export class ExternalService extends Api {
method: 'POST',
}
: undefined;
return await ExternalService.apiCall(this.url, path, values, progress, await this.authHdr(), 'json');
return await ExternalService.apiCall(this.url, path, values, progress, await ConfiguredIdpOAuth.authHdr(this.acctEmail), 'json');
};
}
19 changes: 19 additions & 0 deletions extension/js/common/api/authentication/configured-idp-oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { Catch } from '../../platform/catch.js';
import { InMemoryStoreKeys } from '../../core/const.js';
import { InMemoryStore } from '../../platform/store/in-memory-store.js';
import { AcctStore } from '../../platform/store/acct-store.js';
import { BackendAuthErr } from '../shared/api-error.js';
export class ConfiguredIdpOAuth extends OAuth {
public static newAuthPopupForEnterpriseServerAuthenticationIfNeeded = async (authRes: AuthRes) => {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
Expand All @@ -24,6 +25,24 @@ export class ConfiguredIdpOAuth extends OAuth {
return authRes;
};

public static authHdr = async (acctEmail: string, shouldThrowErrorForEmptyIdToken = true): Promise<{ authorization: string } | undefined> => {
let idToken = await InMemoryStore.getUntilAvailable(acctEmail, InMemoryStoreKeys.ID_TOKEN);
if (idToken) {
const customIDPIdToken = await InMemoryStore.get(acctEmail, InMemoryStoreKeys.CUSTOM_IDP_ID_TOKEN);
// if special JWT is stored in local store, it should be used for Enterprise Server authentication instead of Google JWT
// https://github.com/FlowCrypt/flowcrypt-browser/issues/5799
if (customIDPIdToken) {
idToken = customIDPIdToken;
}
return { authorization: `Bearer ${idToken}` };
}
if (shouldThrowErrorForEmptyIdToken) {
// user will not actually see this message, they'll see a generic login prompt
throw new BackendAuthErr('Missing id token, please re-authenticate');
}
return undefined;
};

public static async newAuthPopup(acctEmail: string, authConf: AuthenticationConfiguration): Promise<AuthRes> {
acctEmail = acctEmail.toLowerCase();
const authRequest = this.newAuthRequest(acctEmail, this.OAUTH_REQUEST_SCOPES);
Expand Down
6 changes: 2 additions & 4 deletions extension/js/common/api/authentication/google/google-oauth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ import { ConfiguredIdpOAuth } from '../configured-idp-oauth.js';
// eslint-disable-next-line @typescript-eslint/naming-convention
type GoogleTokenInfo = { email: string; scope: string; expires_in: number; token_type: string };

/* eslint-enable @typescript-eslint/naming-convention */

export class GoogleOAuth extends OAuth {
public static defaultScopes(group: 'default' | 'contacts' = 'default') {
const { readContacts, readOtherContacts, compose, modify, openid, email, profile } = this.GOOGLE_OAUTH_CONFIG.scopes;
Expand Down Expand Up @@ -97,7 +95,7 @@ export class GoogleOAuth extends OAuth {
};

try {
return performAjaxRequest(req);
return await performAjaxRequest(req);
} catch (firstAttemptErr) {
if (ApiErr.isAuthErr(firstAttemptErr)) {
// force refresh token
Expand Down Expand Up @@ -209,7 +207,7 @@ export class GoogleOAuth extends OAuth {
if (authWindowResult.error) {
return { acctEmail, result: 'Denied', error: authWindowResult.error, id_token: undefined };
}
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion

const uncheckedUrlParams = Url.parse(['scope', 'code', 'state'], authWindowResult.url);
const allowedScopes = Assert.urlParamRequire.string(uncheckedUrlParams, 'scope');
const code = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'code');
Expand Down
6 changes: 3 additions & 3 deletions extension/js/common/api/email-provider/gmail/gmail-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,12 +66,12 @@ export namespace GmailRes {
resultSizeEstimate: number;
};
export type GmailDraftCreate = { id: string };
export type GmailDraftDelete = {}; // eslint-disable-line @typescript-eslint/ban-types
export type GmailDraftUpdate = {}; // eslint-disable-line @typescript-eslint/ban-types
export type GmailDraftDelete = object;
export type GmailDraftUpdate = object;
export type GmailDraftGet = { id: string; message: GmailMsg };
export type GmailDraftMeta = { id: string; message: { id: string; threadId: string } };
export type GmailDraftList = { drafts: GmailDraftMeta[]; nextPageToken: string };
export type GmailDraftSend = {}; // eslint-disable-line @typescript-eslint/ban-types
export type GmailDraftSend = object;
export type GmailAliases = { sendAs: GmailAliases$sendAs[] };
type GmailAliases$sendAs = {
sendAsEmail: string;
Expand Down
4 changes: 2 additions & 2 deletions extension/js/common/api/email-provider/gmail/gmail.ts
Original file line number Diff line number Diff line change
Expand Up @@ -216,7 +216,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
try {
resolve(Buf.fromBase64UrlStr(parsedJsonDataField));
return;
} catch (e) {
} catch {
// the chunk of data may have been cut at an inconvenient index
// shave off up to 50 trailing characters until it can be decoded
parsedJsonDataField = parsedJsonDataField.slice(0, -1);
Expand Down Expand Up @@ -312,7 +312,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
.map(bigNeedle => {
const email = Str.parseEmail(bigNeedle);
if (email?.email) {
const match = email.email.match(/^(.*@.+)\.[^@]+?$/);
const match = /^(.*@.+)\.[^@]+?$/.exec(email.email);
if (match) bigNeedle = match[1]; // omit the top-level domain
}
return bigNeedle.split('.').filter(v => !['com', 'org', 'net'].includes(v));
Expand Down
1 change: 0 additions & 1 deletion extension/js/common/api/email-provider/sendable-msg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,7 +193,6 @@ export class SendableMsg {
this.headers['Reply-To'] = this.replyTo;
}
for (const [recipientType, value] of Object.entries(this.recipients)) {
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
if (value?.length) {
// todo - properly escape/encode this header using emailjs
this.headers[recipientType[0].toUpperCase() + recipientType.slice(1)] = value
Expand Down
Loading

0 comments on commit 8b44889

Please sign in to comment.