Skip to content
Merged
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
4 changes: 3 additions & 1 deletion extension/chrome/elements/backup.htm
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,9 @@
<body class="backup_neutral">
<div id="backup_block" class="backup">
<div class="line">
This backup is protected by your passphrase. Please make sure to note your passphrase down or you may lose access to your encrypted emails!
<span class="backup_message_text" data-test="backup-message-text">
This backup is protected by your passphrase. Please make sure to note your passphrase down or you may lose access to your encrypted emails!
</span>
</div>
<div class="line">
<input class="input_pass_phrase" type="password" id="pass_phrase" placeholder="Enter your passphrase..." maxlength="256" />
Expand Down
25 changes: 19 additions & 6 deletions extension/chrome/elements/backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,22 +12,25 @@ import { View } from '../../js/common/view.js';
import { initPassphraseToggle } from '../../js/common/ui/passphrase-ui.js';
import { KeyStore } from '../../js/common/platform/store/key-store.js';
import { Xss } from '../../js/common/platform/xss.js';
import { AcctStore } from '../../js/common/platform/store/acct-store.js';

View.run(
class BackupView extends View {
private readonly acctEmail: string;
private readonly parentTabId: string;
private readonly frameId: string;
private readonly armoredPrvBackup: string;
private readonly fromEmail?: string;
private storedPrvWithMatchingLongid: KeyInfoWithIdentity | undefined;

public constructor() {
super();
const uncheckedUrlParams = Url.parse(['acctEmail', 'armoredPrvBackup', 'parentTabId', 'frameId']);
const uncheckedUrlParams = Url.parse(['acctEmail', 'armoredPrvBackup', 'parentTabId', 'frameId', 'fromEmail']);
this.acctEmail = Assert.urlParamRequire.string(uncheckedUrlParams, 'acctEmail');
this.parentTabId = Assert.urlParamRequire.string(uncheckedUrlParams, 'parentTabId');
this.frameId = Assert.urlParamRequire.string(uncheckedUrlParams, 'frameId');
this.armoredPrvBackup = Assert.urlParamRequire.string(uncheckedUrlParams, 'armoredPrvBackup');
this.fromEmail = Assert.urlParamRequire.optionalString(uncheckedUrlParams, 'fromEmail');
}

public render = async () => {
Expand All @@ -53,12 +56,22 @@ View.run(
`This private key with fingerprint <span class="green">${Xss.escape(Str.spaced(fingerprint))}</span> has already been imported.`
);
} else {
const { sendAs } = await AcctStore.get(this.acctEmail, ['sendAs']);
const sendAliases = new Set(sendAs ? Object.keys(sendAs) : []);
const notUserOwnedPrvKey = !!this.fromEmail && this.fromEmail !== this.acctEmail && !sendAliases.has(this.fromEmail);
const recommendation = notUserOwnedPrvKey ? '' : 'We recommend importing all backups to ensure you can read all incoming encrypted emails.';
if (notUserOwnedPrvKey) {
$('.backup_message_text')
.html(
`⚠️ This message contains a private key received from ${Xss.escape(this.fromEmail || 'an unknown sender')}. Import only if you intentionally sent this to yourself or received it from your administrator.`
)
.addClass('orange_label'); // xss-safe-value
}
$('.line .private_key_status')
.html(
`The private key <span class="green">${Xss.escape(Str.spaced(fingerprint))}</span> has not been imported yet. \n` +
`We recommend importing all backups to ensure you can read all incoming encrypted emails.`
)
.after('<div class="line"><button class="button green" id="action_import_key">Import Missing Private Key</button></div>'); // xss-direct
.html(`The private key <span class="green">${Xss.escape(Str.spaced(fingerprint))}</span> has not been imported yet. \n` + recommendation) // xss-safe-value
.after(
`<div class="line"><button class="button green" id="action_import_key">${notUserOwnedPrvKey ? 'Import Private Key' : 'Import Missing Private Key'}</button></div>`
); // xss-direct
}
this.sendResizeMsg();
};
Expand Down
6 changes: 6 additions & 0 deletions extension/css/cryptup.css
Original file line number Diff line number Diff line change
Expand Up @@ -1469,6 +1469,12 @@ td {
min-height: 50px;
}

#backup_block .backup_message_text {
font-weight: 500 !important;
font-size: 14px;
padding: 8px 12px;
}

#pgp_block div.error {
margin-bottom: 30px;
margin-left: 12px;
Expand Down
17 changes: 11 additions & 6 deletions extension/js/common/message-renderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -282,7 +282,7 @@ export class MessageRenderer {
renderedXssSafe += frameXssSafe; // xss-safe-value
blocksInFrames[frameId] = block;
} else {
renderedXssSafe += XssSafeFactory.renderableMsgBlock(this.factory, block, isOutgoing); // xss-safe-factory
renderedXssSafe += XssSafeFactory.renderableMsgBlock(this.factory, block, isOutgoing, senderEmail); // xss-safe-factory
}
}
return { renderedXssSafe, isOutgoing, blocksInFrames }; // xss-safe-value
Expand Down Expand Up @@ -385,13 +385,17 @@ export class MessageRenderer {
return 'replaced'; // native should be hidden, custom should appear instead
} else if (treatAs === 'encryptedMsg') {
this.setMsgBodyAndStartProcessing(
loaderContext, treatAs, messageInfo.printMailInfo, messageInfo.from?.email,
renderModule => this.processEncryptedMsgAttachment(a, renderModule, messageInfo.from?.email, messageInfo.isPwdMsgBasedOnMsgSnippet, messageInfo.plainSubject),
loaderContext,
treatAs,
messageInfo.printMailInfo,
messageInfo.from?.email,
renderModule =>
this.processEncryptedMsgAttachment(a, renderModule, messageInfo.from?.email, messageInfo.isPwdMsgBasedOnMsgSnippet, messageInfo.plainSubject),
'append'
);
return 'hidden'; // native attachment should be hidden, the "attachment" goes to the message container
} else if (treatAs === 'privateKey') {
return await this.renderBackupFromFile(a, loaderContext, attachmentSel);
return await this.renderBackupFromFile(a, loaderContext, attachmentSel, messageInfo.from?.email);
} else {
// standard file
loaderContext.renderPlainAttachment(a, attachmentSel);
Expand Down Expand Up @@ -869,11 +873,12 @@ export class MessageRenderer {
private renderBackupFromFile = async (
attachment: Attachment,
loaderContext: LoaderContextInterface,
attachmentSel: JQuery | undefined
attachmentSel: JQuery | undefined,
fromEmail?: string
): Promise<'shown' | 'hidden'> => {
try {
await this.gmail.fetchAttachmentsMissingData([attachment]);
loaderContext.setMsgBody_DANGEROUSLY(this.factory.embeddedBackup(attachment.getData().toUtfStr()), 'append'); // xss-safe-factory
loaderContext.setMsgBody_DANGEROUSLY(this.factory.embeddedBackup(attachment.getData().toUtfStr(), fromEmail), 'append'); // xss-safe-factory
return 'hidden';
} catch {
loaderContext.renderPlainAttachment(attachment, attachmentSel, 'Please reload page'); // todo: unit-test
Expand Down
12 changes: 6 additions & 6 deletions extension/js/common/xss-safe-factory.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,15 +60,15 @@ export class XssSafeFactory {
*
* When edited, REQUEST A SECOND SET OF EYES TO REVIEW CHANGES
*/
public static renderableMsgBlock = (factory: XssSafeFactory, block: MsgBlock, isOutgoing?: boolean) => {
public static renderableMsgBlock = (factory: XssSafeFactory, block: MsgBlock, isOutgoing?: boolean, senderEmail?: string) => {
if (block.type === 'plainText') {
return XssSafeFactory.renderPlainContent(block.content);
} else if (block.type === 'plainHtml') {
return Xss.htmlSanitizeAndStripAllTags(Str.with(block.content), '<br>') + '<br><br>';
} else if (block.type === 'publicKey') {
return factory.embeddedPubkey(PgpArmor.normalize(Str.with(block.content), 'publicKey'), isOutgoing);
} else if (block.type === 'privateKey') {
return factory.embeddedBackup(PgpArmor.normalize(Str.with(block.content), 'privateKey'));
return factory.embeddedBackup(PgpArmor.normalize(Str.with(block.content), 'privateKey'), senderEmail);
} else if (block.type === 'certificate') {
return factory.embeddedPubkey(Str.with(block.content), isOutgoing);
} else if (['encryptedAttachment', 'plainAttachment'].includes(block.type)) {
Expand Down Expand Up @@ -163,8 +163,8 @@ export class XssSafeFactory {
});
};

public srcBackupIframe = (armoredPrvBackup: string) => {
return this.frameSrc(this.extUrl('chrome/elements/backup.htm'), { frameId: this.newId(), armoredPrvBackup });
public srcBackupIframe = (armoredPrvBackup: string, fromEmail?: string) => {
return this.frameSrc(this.extUrl('chrome/elements/backup.htm'), { frameId: this.newId(), armoredPrvBackup, fromEmail });
Comment thread
martgil marked this conversation as resolved.
};

public srcReplyMsgIframe = (convoParams: FactoryReplyParams, skipClickPrompt: boolean, ignoreDraft: boolean) => {
Expand Down Expand Up @@ -229,8 +229,8 @@ export class XssSafeFactory {
return this.iframe(this.srcPgpPubkeyIframe(armoredPubkey, isOutgoing), ['pgp_block', 'publicKey']);
};

public embeddedBackup = (armoredPrvBackup: string) => {
return this.iframe(this.srcBackupIframe(armoredPrvBackup), ['backup_block']);
public embeddedBackup = (armoredPrvBackup: string, fromEmail?: string) => {
Comment thread
martgil marked this conversation as resolved.
return this.iframe(this.srcBackupIframe(armoredPrvBackup, fromEmail), ['backup_block']);
};

public embeddedReply = (convoParams: FactoryReplyParams, skipClickPrompt: boolean, ignoreDraft = false) => {
Expand Down
Loading
Loading