nextcloud / nextcloud/mail

Forwarded message sends the WRONG attachment after changing the sender

Open
#13,177 1 comment 0 reactions 1 assignee View on GitHub

@GretaD is already working on this.

Since Jun 30, 2026.

0. to triage bug
Dominant language
JavaScript
Stars
1k
Forks
348
Avg merge
12h 28m
Merged PRs (30d)
91

Description

Steps to reproduce

Forward an email that has attachments, then change the sender (alias) before sending.
The message is sent with a different attachment than the one displayed — a file
the user never selected, attached, or downloaded. This is a data-confidentiality
issue: the wrong file (potentially another message's attachment) can be transmitted
to the recipient.

  1. Open a received email that has one or more attachments.
  2. Forward it. The original attachments are shown attached to the forward.
  3. Change the From / sender alias on the compose form.
  4. Send.
  5. The sent message (check Sent folder) carries a different attachment than the one shown in the composer.
Expected behavior

The correct Attachment(s) should be sent

Actual behavior
Observed on this instance (server-side evidence)

From nextcloud.log, a forward (subject "Fwd: …", account_id 13) during an active
sender change:

Time (UTC) Request Result
12:30:41 POST /api/drafts InvalidArgumentException: Attachment does not have a type (LocalMessage 131)
12:31:10 POST /api/drafts same error (LocalMessage 132)

The draft auto-save threw (same type-missing path as Bug #1), but the send path is
separate and did not error
, so the message was sent anyway — with a substituted
attachment.

Root cause (analysis)

Forwarded attachments are not copied at forward time. They are stored as a
reference and fetched lazily from IMAP at draft-save / send time:

// AttachmentService::handleForwardedAttachment (lines 376-401)
$mailbox = $this->mailManager->getMailbox($account->getUserId(), $attachment['mailboxId']);
$imapAttachment = $this->messageMapper->getAttachment(
    $client,
    $mailbox->getName(),
    (int)$attachment['uid'],   // source IMAP UID
    $attachment['id'],         // MIME part id
    $account->getUserId(),
);

The reference is {mailboxId, uid, part-id}. IMAP UIDs and MIME part-ids are not
content-pinned
: they identify a coordinate, not a file. When the sender/alias is
changed, the whole local message is re-serialized and these references are re-resolved
against IMAP. If the re-sent reference points at a different UID/part than the original
(stale frontend state, UIDVALIDITY change, part-id collision, or the same type-loss
corruption from Bug #1 mangling the attachment descriptor), getAttachment() returns
whatever file currently lives at that coordinate — which may be an unrelated
message's attachment. That file is then attached and sent.

Impact
  • Confidentiality: a file the sender never selected can be transmitted to a third
    party. If the substituted file belongs to a different message, this is an
    unintended disclosure.
  • Silent: the send path does not surface the draft-save error to the user.
Suggested fixes
  • Materialize forwarded attachments into local storage at forward time (as Bug #1's
    local uploads are), rather than resolving an IMAP coordinate at send time; or pin the
    reference to an immutable identifier (UIDVALIDITY + UID + part, validated before
    fetch).
  • Make handleAttachments() tolerant of a missing type for already-persisted
    attachments instead of throwing, and treat any unresolved reference as a hard send
    failure (never silently substitute).
  • Do not let a draft-save attachment error be bypassed by the send path.
Workaround

Do not change the sender/alias on a forward (or any message) that carries attachments.
On this instance the Mail app has been disabled pending a fix.

Mail app version

5.10.3

Nextcloud version

34.0.0.12

Mailserver or service

Exim4 and Dovecot

Operating system

Debian (Linux 6.12) / Docker (nextcloud:34-apache)

PHP engine version

Other

Nextcloud memory caching

Memcache

Web server

Apache (supported)

Database

MariaDB

Additional info

PHP 8.4.22, Apache
Accounts authenticated via IMAP/SMTP (app-password); LDAP backend for user accounts
MariaDB 11.8

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.