Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion extension/chrome/elements/attachment.ts

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in this file we also print raw error details at

<pre data-test="error-details">${e.stack}\n\nDecryptError:\n${JSON.stringify(e.decryptError, undefined, 2)}</pre>

it can include some malicious code as well, let's perform Xss.escape there too

Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,7 @@ export class AttachmentDownloadView extends View {

private renderHeader = () => {
const span = $(`<span>${this.isEncrypted ? 'ENCRYPTED\n' : 'PLAIN\n'} FILE</span>`);
this.header.empty().append(span); // xss-escaped
this.header.empty().append(span); // xss-direct
};

private getFileIconSrc = () => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -237,11 +237,11 @@ export class ComposeQuoteModule extends ViewModule<ComposeView> {
const header =
`<div ${dirAttr}>` +
`---------- Forwarded message ---------<br/>` +
`From: ${from}<br>` +
`Date: ${dateStr}<br>` +
`Subject: ${this.messageToReplyOrForward.headers.subject}<br>` +
`To: ${this.messageToReplyOrForward.headers.to.join(', ')}<br>` +
(this.messageToReplyOrForward.headers.cc?.length ? `Cc: ${this.messageToReplyOrForward.headers.cc?.join(', ')}` : '') +
`From: ${Xss.escape(from || '')}<br>` +
`Date: ${Xss.escape(dateStr)}<br>` +

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we also have unescaped Date header at

`Subject: ${Xss.escape(this.messageToReplyOrForward.headers.subject || '')}<br>` +
`To: ${Xss.escape(this.messageToReplyOrForward.headers.to.join(', '))}<br>` +
(this.messageToReplyOrForward.headers.cc?.length ? `Cc: ${Xss.escape(this.messageToReplyOrForward.headers.cc.join(', '))}` : '') +
`</div>`;
return `${header}<br><br>${escapedText}`;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -809,7 +809,7 @@ export class ComposeRecipientsModule extends ViewModule<ComposeView> {
ulHtml += '<img class="loading-icon" data-test="pgp-loading-icon" src="/img/svgs/spinner-green-small.svg" />';
contact.pgpLoading
.then(hasPgp => {
Xss.replaceElementDANGEROUSLY($(`[email="${contact.email}"] .loading-icon`)[0], this.getPgpIconHtml(hasPgp)); // xss-escaped
Xss.replaceElementDANGEROUSLY($(`[email="${contact.email}"] .loading-icon`)[0], this.getPgpIconHtml(hasPgp)); // xss-direct
})
.catch(() => {
this.failedLookupEmails.push(contact.email);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ export class PgpBlockViewPrintModule {
html = policy.createHTML(html);
}
if (w?.document?.body) {
w.document.body.innerHTML = html; // xss-escaped
w.document.body.innerHTML = html; // xss-reinsert
}
// Give some time for above dom to load in print dialog
// https://stackoverflow.com/questions/31725373/google-chrome-not-showing-image-in-print-preview
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
'use strict';

import { Catch } from '../../../../js/common/platform/catch.js';
import { Dict } from '../../../../js/common/core/common.js';
import { checkValidURL, Dict } from '../../../../js/common/core/common.js';
import { GmailRes } from '../../../../js/common/api/email-provider/gmail/gmail-parser.js';
import { Google } from '../../../../js/common/api/email-provider/gmail/google.js';
import { InboxView } from '../inbox.js';
Expand Down Expand Up @@ -122,7 +122,7 @@ export class InboxMenuModule extends ViewModule<InboxView> {
if (chooseAccountEl) {
chooseAccountEl.title = this.view.acctEmail;
}
if (this.view.picture) {
if (this.view.picture && checkValidURL(this.view.picture)) {
$('img.main-profile-img')
.attr('src', this.view.picture)
.on(
Expand Down
4 changes: 2 additions & 2 deletions extension/chrome/settings/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import { Bm, BrowserMsg } from '../../js/common/browser/browser-msg.js';
import { Ui } from '../../js/common/browser/ui.js';
import { KeyUtil, KeyInfoWithIdentity } from '../../js/common/core/crypto/key.js';
import { Str, Url, UrlParams } from '../../js/common/core/common.js';
import { checkValidURL, Str, Url, UrlParams } from '../../js/common/core/common.js';
import { ApiErr, EnterpriseServerAuthErr } from '../../js/common/api/shared/api-error.js';
import { Assert } from '../../js/common/assert.js';

Expand Down Expand Up @@ -300,7 +300,7 @@ View.run(
}
this.checkGoogleAcct().catch(Catch.reportErr);
this.checkFcAcctAndContactPage().catch(Catch.reportErr);
if (storage.picture) {
if (storage.picture && checkValidURL(storage.picture)) {
$('img.main-profile-img')
.attr('src', storage.picture)
.on(
Expand Down
2 changes: 1 addition & 1 deletion extension/js/common/ui/key-import-ui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export class KeyImportUi {
for (const option of ['generate_private_key', 'submit_pubkey']) {
const dataTestValue = `input-email-alias-${option}-${e.replace(/[^a-z0-9]+/g, '')}`;
$(`.${option}_addresses`).append(
`<label><input type="checkbox" class="input_email_alias_${option}" data-email="${Xss.escape(e)}" data-name="${sendAs?.[e].name ?? ''}" data-test="${dataTestValue}" />${Xss.escape(e)}</label><br/>`
`<label><input type="checkbox" class="input_email_alias_${option}" data-email="${Xss.escape(e)}" data-name="${Xss.escape(sendAs?.[e].name ?? '')}" data-test="${dataTestValue}" />${Xss.escape(e)}</label><br/>`
); // xss-escaped
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export class ThunderbirdElementReplacer extends WebmailElementReplacer {
}
}
const pgpBlock = this.generatePgpBlockTemplate(encryptionStatus, verificationStatus, decryptedMsg);
$('body').html(pgpBlock); // xss-sanitized
$('body').html(pgpBlock); // xss-escaped
} else {
const decryptErr = result as DecryptError;
let decryptionErrorMsg = '';
Expand All @@ -68,7 +68,7 @@ export class ThunderbirdElementReplacer extends WebmailElementReplacer {
decryptionErrorMsg = `decrypt error: ${(result as DecryptError).error.message}`;
}
const pgpBlock = this.generatePgpBlockTemplate(decryptionErrorMsg, 'not signed', this.emailBodyFromThunderbirdMail);
$('body').html(pgpBlock); // xss-sanitized
$('body').html(pgpBlock); // xss-escaped
}
} else if (this.isCleartextMsg(fullMsg)) {
const message = await openpgp.readCleartextMessage({ cleartextMessage: this.emailBodyFromThunderbirdMail });
Expand All @@ -82,7 +82,7 @@ export class ThunderbirdElementReplacer extends WebmailElementReplacer {
verificationStatus = `could not verify signature: ${result.error}`;
}
const pgpBlock = this.generatePgpBlockTemplate('not encrypted', verificationStatus, signedMessage);
$('body').html(pgpBlock); // xss-sanitized
$('body').html(pgpBlock); // xss-escaped
}
// todo: detached signed message via https://github.com/FlowCrypt/flowcrypt-browser/issues/5668
}
Expand All @@ -93,8 +93,8 @@ export class ThunderbirdElementReplacer extends WebmailElementReplacer {
return `
<div ${encryptionStatus === 'encrypted' ? 'class="pgp_secure"' : 'class="pgp_neutral"'}>
<div>
<div id="pgp_encryption" class="pgp_badge short ${encryptionStatus === 'encrypted' ? 'green_label' : 'red_label'}">${encryptionStatus}</div>
<div id="pgp_signature" class="pgp_badge short ${verificationStatus === 'signed' ? 'green_label' : 'red_label'}">${verificationStatus}</div>
<div id="pgp_encryption" class="pgp_badge short ${encryptionStatus === 'encrypted' ? 'green_label' : 'red_label'}">${Xss.escape(encryptionStatus)}</div>
<div id="pgp_signature" class="pgp_badge short ${verificationStatus === 'signed' ? 'green_label' : 'red_label'}">${Xss.escape(verificationStatus)}</div>
</div>
<div class="pgp_block">
<pre>${Xss.escape(messageToRender)}</pre>
Expand Down
Loading