diff --git a/extension/chrome/dev/ci_unit_test.ts b/extension/chrome/dev/ci_unit_test.ts
index 190b69ebc80..fb09eb6f9cc 100644
--- a/extension/chrome/dev/ci_unit_test.ts
+++ b/extension/chrome/dev/ci_unit_test.ts
@@ -8,6 +8,7 @@ import { AttachmentUI } from '../../js/common/ui/attachment-ui.js';
import { ApiErr } from '../../js/common/api/shared/api-error.js';
import { Mime } from '../../js/common/core/mime.js';
import { Attachment } from '../../js/common/core/attachment.js';
+import { Attester } from '../../js/common/api/key-server/attester.js';
import { Wkd } from '../../js/common/api/key-server/wkd.js';
import { MsgUtil } from '../../js/common/core/crypto/pgp/msg-util.js';
import { Sks } from '../../js/common/api/key-server/sks.js';
@@ -32,6 +33,7 @@ const libs: unknown[] = [
ApiErr,
Attachment,
AttachmentUI,
+ Attester,
Buf,
ExpirationCache,
KeyUtil,
diff --git a/extension/chrome/settings/index.ts b/extension/chrome/settings/index.ts
index 81d18a933a8..eb1338a0c6b 100644
--- a/extension/chrome/settings/index.ts
+++ b/extension/chrome/settings/index.ts
@@ -163,8 +163,7 @@ View.run(
this.setHandler(async () => {
const prvs = await KeyStoreUtil.parse(await KeyStore.getRequired(this.acctEmail!));
const mostUsefulPrv = KeyStoreUtil.chooseMostUseful(prvs, 'EVEN-IF-UNUSABLE');
- const escapedFp = Xss.escape(mostUsefulPrv!.key.id);
- await Settings.renderSubPage(this.acctEmail, this.tabId, 'modules/my_key.htm', `&fingerprint=${escapedFp}`);
+ await Settings.renderSubPage(this.acctEmail, this.tabId, 'modules/my_key.htm', { fingerprint: mostUsefulPrv!.key.id });
})
);
$('.action_show_encrypted_inbox').on(
@@ -538,7 +537,7 @@ View.run(
}
const escapedEmail = Xss.escape(KeyUtil.getPrimaryEmail(prv) || '');
- const escapedLink = `${escapedEmail}`;
+ const escapedLink = `${escapedEmail}`;
const fpHtml = `${Str.spaced(Xss.escape(ki.fingerprints[0]))}`;
const rowClass = isExpired ? 'key-content-row expired' : 'key-content-row';
diff --git a/extension/js/common/api/account-servers/external-service.ts b/extension/js/common/api/account-servers/external-service.ts
index 619ed6096e2..e3c65ef184d 100644
--- a/extension/js/common/api/account-servers/external-service.ts
+++ b/extension/js/common/api/account-servers/external-service.ts
@@ -5,7 +5,7 @@ import { AuthenticationConfiguration } from '../../authentication-configuration.
import { ClientConfigurationError, ClientConfigurationJson } from '../../client-configuration.js';
import { Attachment } from '../../core/attachment.js';
import { Buf } from '../../core/buf.js';
-import { Dict, Str } from '../../core/common.js';
+import { Dict, Str, Url } from '../../core/common.js';
import { FLAVOR, SHARED_TENANT_API_HOST } from '../../core/const.js';
import { ErrorReport } from '../../platform/error-report.js';
import { Serializable } from '../../platform/store/abstract-store.js';
@@ -95,13 +95,18 @@ export class ExternalService extends Api {
public fetchAndSaveClientConfiguration = async (): Promise => {
const auth = await this.request(
- `/api/${this.apiVersion}/client-configuration/authentication?domain=${this.domain}`,
+ Url.create(`/api/${this.apiVersion}/client-configuration/authentication`, { domain: this.domain }),
undefined,
undefined,
false
);
await AcctStore.set(this.acctEmail, { authentication: auth });
- const r = await this.request(`/api/${this.apiVersion}/client-configuration?domain=${this.domain}`, undefined, undefined, false);
+ const r = await this.request(
+ Url.create(`/api/${this.apiVersion}/client-configuration`, { domain: this.domain }),
+ undefined,
+ undefined,
+ false
+ );
if (r.clientConfiguration && !r.clientConfiguration.flags) {
throw new ClientConfigurationError('missing_flags');
}
@@ -173,7 +178,7 @@ export class ExternalService extends Api {
};
public messageGatewayUpdate = async (externalId: string, emailGatewayMessageId: string) => {
- await this.request(`/api/${this.apiVersion}/message/${externalId}/gateway`, {
+ await this.request(`/api/${this.apiVersion}/message/${encodeURIComponent(externalId)}/gateway`, {
fmt: 'JSON',
data: {
emailGatewayMessageId,
@@ -278,5 +283,5 @@ export class ExternalService extends Api {
cc: process(recipients.cc),
bcc: process(recipients.bcc),
};
- }
+ };
}
diff --git a/extension/js/common/api/authentication/google/google-oauth.ts b/extension/js/common/api/authentication/google/google-oauth.ts
index 784786bdf5c..19d062d8d77 100644
--- a/extension/js/common/api/authentication/google/google-oauth.ts
+++ b/extension/js/common/api/authentication/google/google-oauth.ts
@@ -41,7 +41,7 @@ export class GoogleOAuth extends OAuth {
public static async getTokenInfo(accessToken: string): Promise {
return await Api.ajax(
{
- url: `${OAUTH_GOOGLE_API_HOST}/tokeninfo?access_token=${accessToken}`,
+ url: `${OAUTH_GOOGLE_API_HOST}/tokeninfo?${new URLSearchParams([['access_token', accessToken]]).toString()}`,
method: 'GET',
timeout: 10000,
stack: CatchHelper.stackTrace(),
diff --git a/extension/js/common/api/email-provider/gmail/gmail.ts b/extension/js/common/api/email-provider/gmail/gmail.ts
index b62c2453a06..195305ed26e 100644
--- a/extension/js/common/api/email-provider/gmail/gmail.ts
+++ b/extension/js/common/api/email-provider/gmail/gmail.ts
@@ -36,7 +36,12 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
public threadGet = async (threadId: string, format?: GmailResponseFormat, progressCb?: ProgressCb, retryCount = 0): Promise => {
try {
- return await Google.gmailCall(this.acctEmail, `threads/${threadId}`, { method: 'GET', data: { format } }, { download: progressCb });
+ return await Google.gmailCall(
+ this.acctEmail,
+ `threads/${encodeURIComponent(threadId)}`,
+ { method: 'GET', data: { format } },
+ { download: progressCb }
+ );
} catch (e) {
if (ApiErr.isRateLimit(e) && retryCount < MAX_RATE_LIMIT_ERROR_RETRY_COUNT) {
await Time.sleep(1000);
@@ -60,7 +65,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
};
public threadModify = async (id: string, rmLabels: string[], addLabels: string[]): Promise => {
- return await Google.gmailCall(this.acctEmail, `threads/${id}/modify`, {
+ return await Google.gmailCall(this.acctEmail, `threads/${encodeURIComponent(id)}/modify`, {
method: 'POST',
data: {
removeLabelIds: rmLabels || [], // todo - insufficient permission - need https://github.com/FlowCrypt/flowcrypt-browser/issues/1304
@@ -79,11 +84,11 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
};
public draftDelete = async (id: string): Promise => {
- return await Google.gmailCall(this.acctEmail, 'drafts/' + id, { method: 'DELETE' });
+ return await Google.gmailCall(this.acctEmail, `drafts/${encodeURIComponent(id)}`, { method: 'DELETE' });
};
public draftUpdate = async (id: string, mimeMsg: string, threadId: string): Promise => {
- return await Google.gmailCall(this.acctEmail, `drafts/${id}`, {
+ return await Google.gmailCall(this.acctEmail, `drafts/${encodeURIComponent(id)}`, {
method: 'PUT',
data: {
message: { raw: Buf.fromUtfStr(mimeMsg).toBase64UrlStr(), threadId },
@@ -92,7 +97,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
};
public draftGet = async (id: string, format: GmailResponseFormat = 'full'): Promise => {
- return await Google.gmailCall(this.acctEmail, `drafts/${id}`, { method: 'GET', data: { format } });
+ return await Google.gmailCall(this.acctEmail, `drafts/${encodeURIComponent(id)}`, { method: 'GET', data: { format } });
};
public draftList = async (): Promise => {
@@ -139,7 +144,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
try {
return await Google.gmailCall(
this.acctEmail,
- `messages/${msgId}`,
+ `messages/${encodeURIComponent(msgId)}`,
{ method: 'GET', data: { format: format || 'full' } },
progressCb ? { download: progressCb } : undefined
);
@@ -164,10 +169,13 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
};
public attachmentGet = async (attachment: Attachment, progress?: { download: ProgressCb }): Promise => {
+ if (!attachment.msgId || !attachment.id) {
+ throw new Error('Attachment is missing Gmail message id or attachment id');
+ }
type RawGmailAttRes = { attachmentId: string; size: number; data: string };
const { attachmentId, size, data } = await Google.gmailCall(
this.acctEmail,
- `messages/${attachment.msgId}/attachments/${attachment.id}`,
+ `messages/${encodeURIComponent(attachment.msgId)}/attachments/${encodeURIComponent(attachment.id)}`,
{ method: 'GET' },
progress
);
@@ -227,7 +235,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
};
GoogleOAuth.googleApiAuthHeader(this.acctEmail)
.then(async authHeader => {
- const url = `${GMAIL_GOOGLE_API_HOST}/gmail/v1/users/me/messages/${msgId}/attachments/${attachmentId}`;
+ const url = `${GMAIL_GOOGLE_API_HOST}/gmail/v1/users/me/messages/${encodeURIComponent(msgId)}/attachments/${encodeURIComponent(attachmentId)}`;
const response: Response = await fetch(url, {
method: 'GET',
headers: authHeader,
diff --git a/extension/js/common/api/email-provider/gmail/google.ts b/extension/js/common/api/email-provider/gmail/google.ts
index 819f33a918e..13a9391056e 100644
--- a/extension/js/common/api/email-provider/gmail/google.ts
+++ b/extension/js/common/api/email-provider/gmail/google.ts
@@ -11,7 +11,7 @@ import { GoogleOAuth } from '../../authentication/google/google-oauth.js';
import { CatchHelper } from '../../../platform/catch-helper.js';
export class Google {
public static webmailUrl = (acctEmail: string) => {
- return `https://mail.google.com/mail/u/${acctEmail}`;
+ return `https://mail.google.com/mail/u/${encodeURIComponent(acctEmail)}`;
};
public static gmailCall = async