python / python/cpython

email: uncaught IndexError in the header parser on empty-input edge cases

オープン
#151,857 コメント 0 件 リアクション 0 件 担当者 0 名 GitHub で見る

まだ誰も着手していません。

stdlib topic-email type-bug
主要言語
Python
スター
77.2k
フォーク
35.9k
PR マージ指標
PR 指標を取得中

説明

The modern email header parser (email._header_value_parser, used under EmailPolicy / email.policy.default) raises a bare IndexError instead of a HeaderParseError / defect on two malformed inputs. The shared root cause is the same bug class: an empty parser token-list or string is indexed (value[0] / res[-1]) without first checking it is non-empty, so the parser escapes its own error-recovery contract.

Instance 1 — MIME parameter name ending with *
import email, email.policy
email.message_from_string("Content-Type: text/plain; name*\n\n",
                          policy=email.policy.default)['content-type'].params

get_parameter consumes the extended-parameter marker *, leaving value == '', then evaluates value[0]IndexError. parse_mime_parameters only catches HeaderParseError, so the IndexError escapes. Also reachable via name*0* (sectioned) and a trailing x=1; name*.

Instance 2 — address display name that is only a comment
email.message_from_string("To: (c):\n\n", policy=email.policy.default)['to']
email.message_from_string("Cc: (x): a@b.com;\n\n", policy=email.policy.default)['cc']

DisplayName.display_name guards the empty-list case, but when the name is a single cfws token, res.pop(0) empties res and the subsequent res[-1] raises IndexError (this also propagates through the .value property). The second example is a syntactically valid RFC 5322 group whose display name is a comment.

Both are reachable from ordinary parsing of untrusted/malformed address and parameter headers under the modern policies.

Fix

Add minimal guards so each input degrades to the existing recovery path (a parse defect / empty display name), matching how the parser already treats other malformed input.

After the fix, 100k+ randomized message_from_string parses over malformed address and parameter headers produce no escaping non-HeaderParseError exception. (The empty-string IndexError still produced by directly calling the low-level get_* token parsers is their documented non-empty precondition and is not reachable from public header parsing — every caller checks non-empty first.)

Affected versions

Both instances reproduce on main and on the maintained 3.13 / 3.14 / 3.15 bugfix branches; the affected code (get_parameter and DisplayName.display_name) has been present since well before 3.9.

Linked PR

A PR fixing both instances follows.

Linked PRs
  • gh-151858

コントリビューションガイド

コントリビューションガイドを開く

はじめの一歩

  1. issue を最後まで読み、次にプロジェクトのコントリビューションガイドを読みます。
  2. 着手することを issue にコメントします — 二人が同じ作業をするのを防げます。
  3. リポジトリをフォークし、ブランチを切って変更します。
  4. issue 番号を参照したプルリクエストを送ります。

調査の方向性

まず email.policy.default での2つの message_from_string の例を再現し、次に email._header_value_parser の get_parameter と DisplayName.display_name を調べます。空の入力に対するそれらのパスを、既存の HeaderParseError および defect recovery の動作と比較します。完了条件は、壊れた parameter ヘッダーと address ヘッダーが IndexError として外部に伝播しなくなることです。gh-151858 は修正 PR としてすでにリンクされています。

索引モデルが issue の本文から書いたものです。

評価

技術スタック
python
領域
backend
issue の種類
バグ
難易度
2/5
見積もり時間
1〜3時間
活発さ
停滞
明瞭さ
明確に書かれている
初心者へのやさしさ
20/100

新しい issue をメールで受け取る

初心者向けの GitHub issue を短くまとめたダイジェスト。