Description
The attachment reminder scans the entire composer body, including the quoted original
message on replies and forwards. If the quoted (previous) email contains a trigger keyword
(e.g. anexo, attached), the "you may have forgotten an attachment" warning fires even
though the user is not adding any new attachment and never typed the keyword themselves.
This produces frequent false positives on any reply/forward whose original message happens to
mention an attachment — which is common, since the original often did carry one.
Steps to Reproduce
- Ensure the attachment reminder is enabled (default) with default keywords (the list includes
- attached, anexo, em anexo, anhang, ci-joint, etc.).
- Open any email whose body contains one of the keywords (e.g. an email that said "please find attached" / "segue em anexo").
- Click Reply or Forward. Do not type any keyword yourself and do not attach anything
- Click Send.
Expected Behavior
The reminder should scan only the user-authored portion of the message (the new text above
the quote), not the quoted original. Replying to or forwarding a message that mentions an
attachment should not, by itself, trigger the reminder.
Actual Behavior
The attachment-missing warning appears, because the keyword match is found inside the quoted
original, not in anything the user wrote.
Bulwark Version
1.7.6
Stalwart Mail Server Version
0.16.4
Browser
No response
Operating System
None
Screenshots / Screen Recording
No response
Relevant Logs or Error Output
Additional Context
Root cause (code pointer)
components/email/email-composer.tsx (attachment reminder check, ~L1597-1610):
const bodyText = htmlToPlainText(body);
const searchText = ${subject} ${bodyText}.toLowerCase();
const matched = attachmentReminderKeywords.find(kw => searchText.includes(kw.toLowerCase()));
body is the full composer content, which on reply/forward includes the quoted original, so
bodyText contains the quoted text and the keyword search matches against it.
Suggested fix
Strip the quoted original before scanning, so only the user's new text is checked:
HTML mode: the quoted original is wrapped in the atomic quote node marked with
QUOTED_HTML_MARKER = "data-quoted-html" (see components/email/quoted-html.ts). Remove the
div[data-quoted-html] block (parse the body and drop that node) before running
htmlToPlainText.
Plain-text mode: the quoted original is emitted as lines prefixed with > (see the
quoting logic in email-composer.tsx, ~L372). Exclude lines starting with > (and the
"On … wrote:" / "---------- Forwarded message ----------" quote header) from the scanned text.
The subject can still be scanned as-is. Only the new (non-quoted) body should feed the keyword
match.
Notes
Default keyword list is broad and multilingual, so this affects many locales; the Portuguese
entries anexo / em anexo in particular match very commonly.
No existing issue appears to cover this (searched the tracker for attachment-reminder /
quoted / reply-forward false positives).
Description
The attachment reminder scans the entire composer body, including the quoted original
message on replies and forwards. If the quoted (previous) email contains a trigger keyword
(e.g. anexo, attached), the "you may have forgotten an attachment" warning fires even
though the user is not adding any new attachment and never typed the keyword themselves.
This produces frequent false positives on any reply/forward whose original message happens to
mention an attachment — which is common, since the original often did carry one.
Steps to Reproduce
Expected Behavior
The reminder should scan only the user-authored portion of the message (the new text above
the quote), not the quoted original. Replying to or forwarding a message that mentions an
attachment should not, by itself, trigger the reminder.
Actual Behavior
The attachment-missing warning appears, because the keyword match is found inside the quoted
original, not in anything the user wrote.
Bulwark Version
1.7.6
Stalwart Mail Server Version
0.16.4
Browser
No response
Operating System
None
Screenshots / Screen Recording
No response
Relevant Logs or Error Output
Additional Context
Root cause (code pointer)
components/email/email-composer.tsx (attachment reminder check, ~L1597-1610):
const bodyText = htmlToPlainText(body);
const searchText =
${subject} ${bodyText}.toLowerCase();const matched = attachmentReminderKeywords.find(kw => searchText.includes(kw.toLowerCase()));
body is the full composer content, which on reply/forward includes the quoted original, so
bodyText contains the quoted text and the keyword search matches against it.
Suggested fix
Strip the quoted original before scanning, so only the user's new text is checked:
HTML mode: the quoted original is wrapped in the atomic quote node marked with
QUOTED_HTML_MARKER = "data-quoted-html" (see components/email/quoted-html.ts). Remove the
div[data-quoted-html] block (parse the body and drop that node) before running
htmlToPlainText.
Plain-text mode: the quoted original is emitted as lines prefixed with > (see the
quoting logic in email-composer.tsx, ~L372). Exclude lines starting with > (and the
"On … wrote:" / "---------- Forwarded message ----------" quote header) from the scanned text.
The subject can still be scanned as-is. Only the new (non-quoted) body should feed the keyword
match.
Notes
Default keyword list is broad and multilingual, so this affects many locales; the Portuguese
entries anexo / em anexo in particular match very commonly.
No existing issue appears to cover this (searched the tracker for attachment-reminder /
quoted / reply-forward false positives).