fix: tolerate a malformed encoded-word subject; materialize lengths once

decode_header_value's except tuple missed email.errors.HeaderParseError, so a malformed
base64 encoded-word subject (=?utf-8?B?A?=) crashed the OTP scan instead of falling back
to raw - now caught alongside the other decode errors. retrieve_otp consumed lengths once
per fetched message via set(lengths), so a generator was exhausted after the first message
and a later message's fallback code was missed - list() it once up front like folders.

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
2026-07-06 19:24:57 -04:00
parent 9c96fa793a
commit 27d37e8bbb
2 changed files with 3 additions and 1 deletions
+2 -1
View File
@@ -2,6 +2,7 @@
import email.message
import logging
import re
from email.errors import HeaderParseError
from email.header import decode_header, make_header
from typing import Callable, Iterable, Iterator, Optional, Pattern, Sequence, Union
@@ -44,7 +45,7 @@ def decode_header_value(raw: str) -> str:
return raw
try:
return str(make_header(decode_header(raw)))
except (UnicodeDecodeError, LookupError, ValueError) as exc:
except (UnicodeDecodeError, LookupError, ValueError, HeaderParseError) as exc:
log.debug("header decode failed (%s): %s", raw, exc)
return raw
+1
View File
@@ -75,6 +75,7 @@ async def retrieve_otp(
forwarded match on From. set `max_age=None` to disable the freshness check.
"""
folders = list(folders) if folders is not None else list(DEFAULT_FOLDERS)
lengths = list(lengths)
sender_ok = as_predicate(sender)
subject_ok = as_predicate(subject)
query = _server_query(sender, subject, match_field)