Skip to content

[_]: refactor: update encryption handling and address management in email components#71

Merged
jzunigax2 merged 2 commits into
masterfrom
refactor/remove-trial-decryption
Jul 15, 2026
Merged

[_]: refactor: update encryption handling and address management in email components#71
jzunigax2 merged 2 commits into
masterfrom
refactor/remove-trial-decryption

Conversation

@jzunigax2

Copy link
Copy Markdown
Contributor
  • Enhanced the encryption logic to include recipient addresses for better key management, removing trial decryption logic.
  • Updated the versioning of encryption blocks from v1 to v2 across various components and tests.
  • Implemented checks to prevent sending encrypted emails with Bcc recipients.
  • Adjusted related tests to reflect changes in encryption handling and recipient address management.

…components

- Enhanced the encryption logic to include recipient addresses for better key management.
- Updated the versioning of encryption blocks from v1 to v2 across various components and tests.
- Implemented checks to prevent sending encrypted emails with Bcc recipients, ensuring compliance with encryption protocols.
- Adjusted related tests to reflect changes in encryption handling and recipient address management.
@jzunigax2
jzunigax2 requested a review from TamaraFinogina July 14, 2026 23:49
@jzunigax2 jzunigax2 self-assigned this Jul 14, 2026
@jzunigax2
jzunigax2 requested a review from xabg2 as a code owner July 14, 2026 23:49
@jzunigax2 jzunigax2 added the enhancement New feature or request label Jul 14, 2026
@coderabbitai

coderabbitai Bot commented Jul 14, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

The mail encryption envelope was migrated to v2 with recipient-labeled wrapped keys, separate preview encryption, and embedded attachment session keys. Decryption consumers now supply the current address. Encrypted Internxt messages with Bcc are blocked with localized warnings.

Changes

Encrypted mail v2

Layer / File(s) Summary
Build and decrypt v2 envelopes
src/services/mail-encryption/index.ts, src/services/mail-encryption/index.test.ts, src/services/sdk/mail/mail.service.test.ts, src/components/compose-message/hooks/useDraftMessage.test.ts, package.json
Mail encryption now builds v2 body and preview envelopes with recipient-labeled keys, decrypts by address, and stores attachment session keys in the encrypted body payload. Related fixtures and SDK dependency expectations were updated.
Address-aware decryption consumers
src/components/compose-message/hooks/useComposeSend.ts, src/components/compose-message/index.tsx, src/hooks/mail/*
Attachment recovery and preview hooks require both current keys and address, pass the address to decryption methods, and rerun effects when it changes.
Encrypted Bcc send guard
src/components/compose-message/hooks/useComposeSend.ts, src/components/compose-message/hooks/useComposeSend.test.ts, src/i18n/locales/{en,es,fr,it}.json
Encrypted Internxt sends containing Bcc recipients are stopped before encryption or dispatch and show localized warning text.

Estimated code review effort: 4 (Complex) | ~45 minutes

Suggested reviewers: xabg2, xabg2, tamarafinogina

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title matches the main change: encryption handling and address management updates in email components.
Description check ✅ Passed The description is clearly related to the encryption, versioning, Bcc checks, and test updates in the pull request.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor/remove-trial-decryption

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install timed out. The project may have too many dependencies for the sandbox.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 15, 2026

Copy link
Copy Markdown

Deploying mail-web with  Cloudflare Pages  Cloudflare Pages

Latest commit: 4b7bbf4
Status: ✅  Deploy successful!
Preview URL: https://9b922ee2.mail-web-ea0.pages.dev
Branch Preview URL: https://refactor-remove-trial-decryp.mail-web-ea0.pages.dev

View logs

@sonarqubecloud

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Warning

CodeRabbit couldn't request changes on this pull request because it doesn't have sufficient GitHub permissions.

Please grant CodeRabbit Pull requests: Read and write permission and re-run the review.

👉 Steps to fix this

Actionable comments posted: 3

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/components/compose-message/hooks/useComposeSend.ts`:
- Around line 96-100: Update the send function to check the result of
handleInheritAttachments immediately after awaiting it, and return early when it
is undefined before dispatching the email; preserve normal sending when
attachment recovery succeeds.

In `@src/hooks/mail/useAttachmentsSessionKey.ts`:
- Around line 20-24: Update the cache guard and cache entries in the effect
within useAttachmentsSessionKey so cached results are keyed by the normalized
address in addition to mailId, preventing reuse across account switches; include
envelope and keypair identity as cache inputs where available. Preserve existing
success and failure handling, and add a regression test covering an address
change that must not reuse the previous cached entry.

In `@src/hooks/mail/useDecryptedPreviews.ts`:
- Around line 9-21: Update decryptPendingPreviews to start all
decryptSummaryPreview operations concurrently and await them with Promise.all,
then populate the resolved record while preserving each summary.id mapping and
existing error-handling behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 72a0ff4c-3765-491a-8c8f-bdb5ef6db84f

📥 Commits

Reviewing files that changed from the base of the PR and between d530e5e and 4b7bbf4.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (16)
  • package.json
  • src/components/compose-message/hooks/useComposeSend.test.ts
  • src/components/compose-message/hooks/useComposeSend.ts
  • src/components/compose-message/hooks/useDraftMessage.test.ts
  • src/components/compose-message/index.tsx
  • src/hooks/mail/useAttachmentsSessionKey.test.tsx
  • src/hooks/mail/useAttachmentsSessionKey.ts
  • src/hooks/mail/useDecryptedPreviews.test.tsx
  • src/hooks/mail/useDecryptedPreviews.ts
  • src/i18n/locales/en.json
  • src/i18n/locales/es.json
  • src/i18n/locales/fr.json
  • src/i18n/locales/it.json
  • src/services/mail-encryption/index.test.ts
  • src/services/mail-encryption/index.ts
  • src/services/sdk/mail/mail.service.test.ts

Comment on lines 96 to +100

if (pendingInherited.length > 0) {
const senderKeysForAttachments = MailKeysService.instance.getCurrentKeys();
if (!senderKeysForAttachments) {
const senderAddress = MailKeysService.instance.getCurrentAddress();
if (!senderKeysForAttachments || !senderAddress) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🗄️ Data Integrity & Integration | 🔴 Critical | ⚡ Quick win

Abort email dispatch if inherited attachments fail to process.

When handleInheritAttachments encounters missing keys, missing addresses, or decryption errors, it displays an error toast and returns undefined. However, the send function executes const attachmentsToSend = await handleInheritAttachments(); and immediately proceeds without verifying if the result is undefined.

This causes the email to be silently sent without its attachments if processing fails. To prevent this data loss, verify the return value in the send function and abort the operation if the attachment recovery failed.

    // In `send` (around line 183):
    const attachmentsToSend = await handleInheritAttachments();
    if (!attachmentsToSend) return;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/components/compose-message/hooks/useComposeSend.ts` around lines 96 -
100, Update the send function to check the result of handleInheritAttachments
immediately after awaiting it, and return early when it is undefined before
dispatching the email; preserve normal sending when attachment recovery
succeeds.

Comment on lines 20 to 24
const [cache, setCache] = useState<Record<string, CachedKey>>({});

useEffect(() => {
if (!mailId || !envelope || !keypair) return;
if (!mailId || !envelope || !keypair || !address) return;
if (cache[mailId]) return;

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Key the cache by all decryption inputs, not only mailId.

When the address changes, the effect reruns but immediately accepts the previous address’s cached entry. This can return a stale cross-account session key—or retain a prior failure. Include at least the normalized address, and ideally envelope/keypair identity, in the cache entry and add an address-switch regression test.

Also applies to: 40-40

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/mail/useAttachmentsSessionKey.ts` around lines 20 - 24, Update the
cache guard and cache entries in the effect within useAttachmentsSessionKey so
cached results are keyed by the normalized address in addition to mailId,
preventing reuse across account switches; include envelope and keypair identity
as cache inputs where available. Preserve existing success and failure handling,
and add a regression test covering an address change that must not reuse the
previous cached entry.

Comment on lines +9 to +21
const decryptPendingPreviews = async (
pending: Summary[],
keypair: HybridKeyPair,
address: string,
): Promise<Record<string, string>> => {
const resolved: Record<string, string> = {};
for (const summary of pending) {
try {
resolved[summary.id] = await MailEncryptionService.instance.decryptSummaryPreview(summary.encryption!, keypair);
resolved[summary.id] = await MailEncryptionService.instance.decryptSummaryPreview(
summary.encryption!,
keypair,
address,
);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick win

Process preview decryptions concurrently.

Currently, the loop processes preview decryptions sequentially. Since WebCrypto operations are asynchronous and offloaded to the browser's crypto thread, using Promise.all to decrypt them concurrently will significantly improve performance when rendering a view with multiple encrypted previews.

♻️ Proposed refactor
-const decryptPendingPreviews = async (
-  pending: Summary[],
-  keypair: HybridKeyPair,
-  address: string,
-): Promise<Record<string, string>> => {
-  const resolved: Record<string, string> = {};
-  for (const summary of pending) {
-    try {
-      resolved[summary.id] = await MailEncryptionService.instance.decryptSummaryPreview(
-        summary.encryption!,
-        keypair,
-        address,
-      );
-    } catch (error) {
-      console.error('Failed to decrypt mail preview', { mailId: summary.id, error });
-    }
-  }
-  return resolved;
-};
+const decryptPendingPreviews = async (
+  pending: Summary[],
+  keypair: HybridKeyPair,
+  address: string,
+): Promise<Record<string, string>> => {
+  const resolved: Record<string, string> = {};
+  await Promise.all(
+    pending.map(async (summary) => {
+      try {
+        resolved[summary.id] = await MailEncryptionService.instance.decryptSummaryPreview(
+          summary.encryption!,
+          keypair,
+          address,
+        );
+      } catch (error) {
+        console.error('Failed to decrypt mail preview', { mailId: summary.id, error });
+      }
+    })
+  );
+  return resolved;
+};
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const decryptPendingPreviews = async (
pending: Summary[],
keypair: HybridKeyPair,
address: string,
): Promise<Record<string, string>> => {
const resolved: Record<string, string> = {};
for (const summary of pending) {
try {
resolved[summary.id] = await MailEncryptionService.instance.decryptSummaryPreview(summary.encryption!, keypair);
resolved[summary.id] = await MailEncryptionService.instance.decryptSummaryPreview(
summary.encryption!,
keypair,
address,
);
const decryptPendingPreviews = async (
pending: Summary[],
keypair: HybridKeyPair,
address: string,
): Promise<Record<string, string>> => {
const resolved: Record<string, string> = {};
await Promise.all(
pending.map(async (summary) => {
try {
resolved[summary.id] = await MailEncryptionService.instance.decryptSummaryPreview(
summary.encryption!,
keypair,
address,
);
} catch (error) {
console.error('Failed to decrypt mail preview', { mailId: summary.id, error });
}
})
);
return resolved;
};
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/hooks/mail/useDecryptedPreviews.ts` around lines 9 - 21, Update
decryptPendingPreviews to start all decryptSummaryPreview operations
concurrently and await them with Promise.all, then populate the resolved record
while preserving each summary.id mapping and existing error-handling behavior.

@jzunigax2
jzunigax2 merged commit 0d747a9 into master Jul 15, 2026
6 checks passed
@jzunigax2
jzunigax2 deleted the refactor/remove-trial-decryption branch July 15, 2026 11:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants