fix: revert OTP-in-logs (spent on arrival, not a secret); F1 ContentTypeError, F5 callable None-guard

revert the M-1 log change — a single-use OTP is consumed on arrival, not a live secret,
so log the code value again. keep the oauth error-body truncate.

F1: oauth token fetch uses resp.json(content_type=None) so a 200 with text/plain doesn't
ContentTypeError and discard a valid token. F5: as_predicate coalesces None for the
callable branch like the string/regex branches. drop a redundant digits.isdigit().

Signed-off-by: disqualifier <dev@disqualifier.me>
This commit is contained in:
disqualifier 2026-06-29 21:34:35 -04:00
parent f940641a5a
commit 3da833f2fc
3 changed files with 10 additions and 7 deletions

View File

@ -76,7 +76,7 @@ def _scan(text: str, patterns: list[Pattern], lengths: set[int]) -> Optional[str
return m.group(1) if m.groups() else m.group(0)
for token in re.split(r"\s+", text):
digits = "".join(c for c in token if c.isdigit())
if digits and len(digits) in lengths and digits.isdigit():
if digits and len(digits) in lengths:
return digits
return None
@ -121,6 +121,8 @@ def as_predicate(spec: MatchSpec) -> Callable[[Optional[str]], bool]:
if isinstance(spec, re.Pattern):
return lambda value: bool(spec.search(value or ""))
if callable(spec):
return spec
# coalesce None like the string/regex branches so the documented Optional[str]
# predicate contract holds even if a caller's callable assumes a real string
return lambda value: bool(spec(value or ""))
needle = str(spec).lower()
return lambda value: needle in (value or "").lower()

View File

@ -76,7 +76,10 @@ class _RefreshTokenProvider:
async with aiohttp.ClientSession(timeout=timeout) as session:
async with session.post(endpoint, data=data) as resp:
if resp.status == 200:
token = (await resp.json()).get("access_token")
# content_type=None: some token endpoints return a 200 with
# text/plain or text/javascript; default json() would raise
# ContentTypeError and discard a valid token body
token = (await resp.json(content_type=None)).get("access_token")
if token:
self._failures = 0
return token

View File

@ -113,14 +113,12 @@ async def retrieve_otp(
if max_age is not None:
age = _age_seconds(message)
if age is not None and age > max_age:
log.info("code skipped, too old (%.0fs > %.0fs)", age, max_age)
log.info("code %s skipped, too old (%.0fs > %.0fs)", code, age, max_age)
continue
if mark_seen:
await client.mark_seen(email_id)
# never log the code value — it's a live single-use secret that would
# reach any aggregation/retention sink the host wires up
log.info("found code in %s", folder)
log.info("found code %s in %s", code, folder)
return code
if attempt < retries: