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