Skip to content

Commit 211be40

Browse files
authored
Merge branch 'master' into issue-5668-add-thunderbird-attachments-support
2 parents e3a9756 + 132dafd commit 211be40

File tree

21 files changed

+672
-351
lines changed

21 files changed

+672
-351
lines changed

conf/tsconfig.content_scripts.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,9 @@
2020
"openpgp": ["../node_modules/openpgp/openpgp.d.ts"],
2121
"@openpgp/web-stream-tools": ["../node_modules/@openpgp/web-stream-tools/lib/index.d.ts"],
2222
"squire-rte": ["../node_modules/squire-rte/dist/types/Squire.d.ts"],
23-
"undici-types": ["../node_modules/undici-types/index.d.ts", "COMMENT"]
23+
"undici-types": ["../node_modules/undici-types/index.d.ts", "COMMENT"],
24+
"linkifyHtml": ["../node_modules/linkify-html/dist/linkify-html.es.d.ts", "COMMENT"],
25+
"linkifyjs": ["../node_modules/linkifyjs/dist/linkify.es.d.ts", "COMMENT"]
2426
},
2527
"typeRoots": ["../extension/types", "../extension/js/common/core/types"]
2628
},

extension/chrome/elements/compose-modules/compose-err-module.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ import { ViewModule } from '../../../js/common/view-module.js';
1616
import { ComposeView } from '../compose.js';
1717
import { AjaxErrMsgs } from '../../../js/common/api/shared/api-error.js';
1818
import { Lang } from '../../../js/common/lang.js';
19+
import linkifyHtml from 'linkifyHtml';
20+
import { MsgUtil } from '../../../js/common/core/crypto/pgp/msg-util.js';
1921

2022
export class ComposerUserError extends Error {}
2123
class ComposerNotReadyError extends ComposerUserError {}
@@ -160,6 +162,11 @@ export class ComposeErrModule extends ViewModule<ComposeView> {
160162
};
161163

162164
public throwIfEncryptionPasswordInvalidOrDisabled = async ({ subject, pwd }: { subject: string; pwd?: string }) => {
165+
const disallowedPasswordMessageTerms = this.view.clientConfiguration.getDisallowPasswordMessagesForTerms();
166+
const disallowedPasswordMessageErrorText = this.view.clientConfiguration.getDisallowPasswordMessagesErrorText();
167+
if (disallowedPasswordMessageErrorText && disallowedPasswordMessageTerms && !MsgUtil.isPasswordMessageEnabled(subject, disallowedPasswordMessageTerms)) {
168+
throw new ComposerUserError(linkifyHtml(disallowedPasswordMessageErrorText, { target: '_blank' }));
169+
}
163170
// When DISABLE_FLOWCRYPT_HOSTED_PASSWORD_MESSAGES present, and recipients are missing a public key, and the user is using flowcrypt.com/shared-tenant-fes (not FES)
164171
if (this.view.clientConfiguration.shouldDisableFlowCryptHostedPasswordMessages() && !this.view.isCustomerUrlFesUsed()) {
165172
throw new ComposerUserError(Lang.compose.addMissingRecipientPubkeys);

extension/chrome/elements/compose-modules/compose-storage-module.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -146,7 +146,7 @@ export class ComposeStorageModule extends ViewModule<ComposeView> {
146146
// re-query the storage, which is now updated
147147
const updatedContact = await ContactStore.getOneWithAllPubkeys(undefined, email);
148148
this.view.errModule.debug(`getUpToDatePubkeys.updatedContact.sortedPubkeys.length(${updatedContact?.sortedPubkeys.length})`);
149-
this.view.errModule.debug(`getUpToDatePubkeys.updatedContact(${updatedContact})`);
149+
this.view.errModule.debug(`getUpToDatePubkeys.updatedContact(${JSON.stringify(updatedContact)})`);
150150
return updatedContact;
151151
};
152152

extension/chrome/elements/compose.htm

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ <h1 id="header_title" data-test="header-title">New Secure Message</h1>
254254
<script src="/lib/zxcvbn.js"></script>
255255
<script src="/lib/iso-8859-2.js"></script>
256256
<script src="/lib/forge.js"></script>
257+
<script src="/lib/linkify.min.js"></script>
258+
<script src="/lib/linkify-html.min.js"></script>
257259
<script src="compose.js" type="module"></script>
258260
</body>
259261
</html>

extension/js/common/api/email-provider/gmail/gmail.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ export class Gmail extends EmailProviderApi implements EmailProviderInterface {
253253
// Depending on your needs, you might throw an error or handle this scenario differently.
254254
throw new Error('Failed to meet the minimum byte requirement or condition.');
255255
})
256-
// eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable
256+
257257
.catch(reject);
258258
});
259259
};

extension/js/common/api/shared/api.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -314,7 +314,7 @@ export class Api {
314314
.then(data => {
315315
resolve(data as FetchResult<T, RT>);
316316
})
317-
// eslint-disable-next-line @typescript-eslint/use-unknown-in-catch-callback-variable
317+
318318
.catch(reject);
319319
});
320320
} catch (e) {

extension/js/common/browser/browser-extension.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ export class BrowserExtension {
2020
try {
2121
bugReport.details = JSON.stringify(details, undefined, 2);
2222
} catch (e) {
23-
bugReport.details_as_string = String(details);
23+
bugReport.details_as_string = String(details as unknown);
2424
bugReport.details_serialization_error = String(e);
2525
}
2626
let result = '';

extension/js/common/client-configuration.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,8 @@ export type ClientConfigurationJson = {
3333
enforce_keygen_expire_months?: number;
3434
in_memory_pass_phrase_session_length?: number;
3535
prv_backup_to_designated_mailbox?: string;
36+
disallow_password_messages_for_terms?: string[];
37+
disallow_password_messages_error_text?: string;
3638
};
3739
/* eslint-enable @typescript-eslint/naming-convention */
3840

@@ -110,6 +112,21 @@ export class ClientConfiguration {
110112
return this.clientConfigurationJson.enforce_keygen_expire_months;
111113
};
112114

115+
/**
116+
* An array of strings to check against the subject of the composed password-protected message.
117+
* If any string in this array is found in the subject, an error alert must be displayed.
118+
*/
119+
public getDisallowPasswordMessagesForTerms = (): string[] | undefined => {
120+
return this.clientConfigurationJson.disallow_password_messages_for_terms?.filter(term => !!term);
121+
};
122+
123+
/**
124+
* The text to be displayed in the password message terms error alert
125+
*/
126+
public getDisallowPasswordMessagesErrorText = (): string | undefined => {
127+
return this.clientConfigurationJson.disallow_password_messages_error_text;
128+
};
129+
113130
/**
114131
* pass phrase session length to be configurable with client configuraiton
115132
* default 4 hours

extension/js/common/core/common.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,7 +129,7 @@ export class Str {
129129
};
130130

131131
public static prettyPrint = (obj: unknown) => {
132-
return typeof obj === 'object' ? JSON.stringify(obj, undefined, 2).replace(/ /g, '&nbsp;').replace(/\n/g, '<br />') : String(obj);
132+
return typeof obj === 'object' ? JSON.stringify(obj, undefined, 2).replace(/ /g, '&nbsp;').replace(/\n/g, '<br />') : String(obj as unknown);
133133
};
134134

135135
public static normalizeSpaces = (str: string) => {

extension/js/common/core/crypto/pgp/msg-util.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -306,6 +306,31 @@ export class MsgUtil {
306306
return diagnosis;
307307
}
308308

309+
public static isPasswordMessageEnabled(subject: string, disallowTerms: string[]) {
310+
if (!subject || !Array.isArray(disallowTerms)) {
311+
return true; // If no subject or no terms to disallow, assume enabled
312+
}
313+
314+
const lowerSubject = subject.toLowerCase();
315+
316+
for (const term of disallowTerms) {
317+
// Escape term for regex
318+
const escapedTerm = term.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
319+
// Use regex to ensure the term appears as a separate token
320+
// (^|\W) ensures the term is at start or preceded by non-word char
321+
// (\W|$) ensures the term is followed by non-word char or end
322+
const regex = new RegExp(`(^|\\W)${escapedTerm}(\\W|$)`, 'i');
323+
324+
if (regex.test(lowerSubject)) {
325+
// Found a disallowed term as a separate token
326+
return false;
327+
}
328+
}
329+
330+
// No disallowed terms found as exact matches
331+
return true;
332+
}
333+
309334
private static async getSortedKeys(kiWithPp: KeyInfoWithIdentityAndOptionalPp[], msg: OpenPGP.Message<OpenPGP.Data>): Promise<SortedKeysForDecrypt> {
310335
const keys: SortedKeysForDecrypt = {
311336
encryptedFor: [],

0 commit comments

Comments
 (0)