email: RFC 2047 encoded-word in an addr-spec local-part corrupts address parsing
還沒有人認領這個 Issue。
- 主要語言
- Python
- 星號
- 77.2k
- 分支
- 36k
- 平均合併
- 1 天 9 小時
- 30 天內合併 PR
- 558
描述
Bug report
The modern email parser (email._header_value_parser, used by email.policy.default and email.headerregistry) decodes an RFC 2047 encoded-word that appears inside an addr-spec local-part, and for an obsolete local-part it then re-parses the decoded text. RFC 2047 §5 forbids encoded-words in an addr-spec, and re-parsing decoded text corrupts the result — most visibly when the encoded-word decodes to a character that is special in the address grammar (e.g. =40 decodes to @).
Reproducers
Reproduced on current main (3.16.0a0); the code is unchanged since the parser was introduced, so all maintained versions are affected. The legacy compat32 parser does not decode encoded-words during address parsing and is unaffected.
An encoded-word in the local-part is silently decoded (already a violation of RFC 2047 §5):
>>> from email import message_from_string, policy
>>> m = message_from_string("To: =?utf-8?q?admin?=@example.com\n\n", policy=policy.default)
>>> m['to'].addresses[0].username
'admin'
When the encoded-word decodes to a bare special, the re-parse misaligns and the address is silently corrupted to the null address:
>>> # =40 is the Q-encoding of '@'
>>> m = message_from_string("To: =?utf-8?q?a=40b?=c@host.com\n\n", policy=policy.default)
>>> m['to'].addresses[0]
Address(display_name='', username='', domain='')
The same input through headerregistry.Address leaks an internal parse error:
>>> from email.headerregistry import Address
>>> Address(addr_spec='=?utf-8?q?a=40b?=c@host.com')
Traceback (most recent call last):
...
email.errors.HeaderParseError: Invalid Domain
Cause
get_local_part() parses an obsolete local-part by rendering the already-parsed tokens back to text with str() and re-parsing that text:
obs_local_part, value = get_obs_local_part(str(local_part) + value)
str(local_part) decodes any encoded-word, so the text handed back to get_obs_local_part() differs from the source. When the decoded text contains a special such as @, the re-parse stops in the middle of the rendered prefix, leaving spurious decoded characters in the remainder and corrupting the address (and, downstream, raising Invalid Domain).
Per RFC 2047 §5 an encoded-word may only appear in place of text, inside a comment, or in place of a word within a phrase (the display-name) — never inside an addr-spec. So the local-part should not be decoded at all.
Relationship to GH-136063
Found while working on GH-136063: get_local_part is one of the quadratic-complexity spots there. The fix re-parses the obsolete local-part from the original source text instead of from the decoded str() rendering, which also removes this corruption — an encoded-word in a local-part is then simply reported as an invalid address rather than decoded and misparsed.
Linked PRs
- gh-152520
貢獻指南
從這裡開始
- 先讀完整個 Issue,再讀專案的貢獻指南。
- 在 Issue 下留言說明你要接手 —— 這能避免兩個人做同樣的事。
- Fork 儲存庫,在一個分支上完成修改。
- 送出 Pull Request,並在描述裡引用這個 Issue 編號。
研究方向
從 email._header_value_parser 中的 get_local_part() 開始,將其對過時 local-parts 的處理與原始原始碼文字和解碼後的 str() 表示進行比較。使用 issue 中的 RFC 2047 範例,驗證 addr-spec local-parts 中的 encoded-words 會在不造成損毀或內部剖析錯誤的情況下被拒絕;gh-152520 已經與這項工作連結。
由索引模型根據 Issue 內容生成。
評估
- 技術堆疊
- python
- 領域
- backend
- Issue 類型
- 缺陷
- 難度
- 3/5
- 預估耗時
- 1-2 天
- 活躍度
- 停滯
- 描述清晰度
- 描述清楚
- 新手友好度
- 25/100