Linux maintainer tooling and workflows
 help / color / mirror / Atom feed
From: "Alexis Lothoré" <alexis.lothore@bootlin.com>
To: tools@linux.kernel.org
Cc: "Thomas Petazzoni" <thomas.petazzoni@bootlin.com>,
	"Alexis Lothoré" <alexis.lothore@bootlin.com>
Subject: [PATCH] git-patchwork-bot: prevent wrong encoded split in To: field
Date: Mon, 24 Aug 2026 15:34:56 +0200	[thread overview]
Message-ID: <20260824133456.23805-1-alexis.lothore@bootlin.com> (raw)

notify_submitters() build the submitter To: header by RFC 2047-encoding
the entire 'Name <addr>' string through email.header.Header(). For long
non-ASCII display names the encoded-word exceeds the 75-character
folding limit and is split mid-token, shredding the adjacent address;
MTAs may then interpret the leftover fragments as bare local-parts and
append a hostname, producing a mangled and undeliverable To: header
(e.g.  '...@aws-us-west-2-korg-oddjob- rhel9-1.codeaurora.org').

Add the _format_recipient() helper which uses email.utils.formataddr()
to encode only the display name while keeping the bare <addr> literal
and intact.  While at it, make sure to use the same logic for both
branches generating the 'To' field. Add a small test validating that the
address part remains untouched.

Signed-off-by: Alexis Lothoré <alexis.lothore@bootlin.com>
---
An example of the issue in the wild can be found in [1], resulting in
the submitter never receiving the merge confirmation mail.

[1] https://lore.kernel.org/bpf/178734001048.1447337.676425481174448454.git-patchwork-notify@kernel.org/
---
 git-patchwork-bot.py        | 12 +++++++++---
 tests/test_patchwork_bot.py | 25 +++++++++++++++++++++++++
 2 files changed, 34 insertions(+), 3 deletions(-)

diff --git a/git-patchwork-bot.py b/git-patchwork-bot.py
index 0fa112e85262..2aa5b2db6037 100755
--- a/git-patchwork-bot.py
+++ b/git-patchwork-bot.py
@@ -32,7 +32,7 @@ import time
 from email import charset
 from email.header import Header
 from email.mime.text import MIMEText
-from email.utils import formatdate, getaddresses, make_msgid
+from email.utils import formataddr, formatdate, getaddresses, make_msgid
 from fcntl import LOCK_EX, LOCK_NB, lockf
 from string import Template
 from typing import Any, Dict, List, Optional, Set, Tuple, Union
@@ -998,6 +998,12 @@ def send_summary(serieslist: List[Dict[str, Any]],
     return str(msg['Message-Id'])
 
 
+def _format_recipient(name: Optional[str], addr: str) -> str:
+    """Build a 'Name <addr>' header value, encoding only the display name."""
+
+    return formataddr((name or '', addr))
+
+
 def get_tweaks(pconfig: Dict[str, str], hconfig: Dict[str, Any]) -> Dict[str, str]:
     fields = ['from', 'summaryto', 'onlyto', 'neverto', 'onlyifcc', 'neverifcc',
               'alwayscc', 'alwaysbcc', 'cclist', 'ccall']
@@ -1155,10 +1161,10 @@ def notify_submitters(serieslist: List[Dict[str, Any]],
 
         if 'onlyto' in tweaks:
             targets = listify(tweaks['onlyto'])
-            msg['To'] = '%s <%s>' % (submitter.get('name'), targets[0])
+            msg['To'] = _format_recipient(submitter.get('name'), targets[0])
         else:
             targets = [submitter.get('email')]
-            msg['To'] = Header('%s <%s>' % (submitter.get('name'), submitter.get('email'))).encode()
+            msg['To'] = _format_recipient(submitter.get('name'), submitter.get('email'))
 
             ccaddrs: List[str] = list()
             if tweaks.get('alwayscc'):
diff --git a/tests/test_patchwork_bot.py b/tests/test_patchwork_bot.py
index 2a6a6c53a887..658dbe523d8d 100644
--- a/tests/test_patchwork_bot.py
+++ b/tests/test_patchwork_bot.py
@@ -488,6 +488,31 @@ class TestGetTweaks:
             assert result[f] == f'p-{f}'
 
 
+class TestFormatRecipient:
+    """Tests for _format_recipient() header construction."""
+
+    def test_ascii_name_and_addr(self):
+        value = pwbot._format_recipient('Alexis Lothe', 'alexis.lothore@bootlin.com')
+        assert value == 'Alexis Lothe <alexis.lothore@bootlin.com>'
+
+    def test_name_without_addr_falls_back(self):
+        value = pwbot._format_recipient(None, 'alexis.lothore@bootlin.com')
+        assert value == 'alexis.lothore@bootlin.com'
+
+        value = pwbot._format_recipient('', 'alexis.lothore@bootlin.com')
+        assert value == 'alexis.lothore@bootlin.com'
+
+    def test_nonascii_name_keeps_addr_intact(self):
+        name = 'Alexis Lothoré (eBPF Foundation)'
+        addr = 'alexis.lothore@bootlin.com'
+        value = pwbot._format_recipient(name, addr)
+        from email.utils import getaddresses
+        (parsed_name, parsed_addr), = getaddresses([value])
+        assert parsed_addr == addr
+        assert addr in value
+        assert '<%s>' % addr in value
+
+
 class TestGitGetNewRevs:
     """Tests for git_get_new_revs() with mocked git output."""
 
-- 
2.55.0


                 reply	other threads:[~2026-08-24 13:35 UTC|newest]

Thread overview: [no followups] expand[flat|nested]  mbox.gz  Atom feed

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20260824133456.23805-1-alexis.lothore@bootlin.com \
    --to=alexis.lothore@bootlin.com \
    --cc=thomas.petazzoni@bootlin.com \
    --cc=tools@linux.kernel.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox