Skip to content
Open
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
24 changes: 18 additions & 6 deletions extension/chrome/elements/backup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,17 @@ View.run(
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.string(uncheckedUrlParams, 'fromEmail');
}

public render = async () => {
Expand All @@ -53,12 +55,22 @@ View.run(
`This private key with fingerprint <span class="green">${Xss.escape(Str.spaced(fingerprint))}</span> has already been imported.`
);
} else {
const notUserOwnedPrvKey = this.fromEmail !== this.acctEmail;
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.

what about user email aliases? here we check only main user email, but email aliases will probably show warning message. in MessageRenderer we have isOutgoing method which checks array of sendAs aliases, I think we should use similar check here too:

return Boolean(senderEmail && this.sendAsAliases.has(senderEmail));

const recommendation = notUserOwnedPrvKey ? '' : 'We recommend importing all backups to ensure you can read all incoming encrypted emails.';
if (notUserOwnedPrvKey) {
if (notUserOwnedPrvKey) {
Comment on lines 60 to +61
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.

duplicated if (notUserOwnedPrvKey) { :)

$('.backup_message_text')
.html(
`⚠️ This message contains a private key received from ${Xss.escape(this.fromEmail)}. 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 });
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.

fromEmail is required property in backup.ts (https://github.com/FlowCrypt/flowcrypt-browser/blob/issue-6199-fix-improper-backup-detection/extension/chrome/elements/backup.ts#L22), but here optional value fromEmail?: string is passed.
what happens if undefined value is passed? backup.htm will probably fail to render, could you please check?

};

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) => {
return this.iframe(this.srcBackupIframe(armoredPrvBackup, fromEmail), ['backup_block']);
};

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