Validate cell characters and digit/period sanity

Halt import on control characters or a ':'/'/' inside an issuer or
account (they collide with the OATH issuer:account separator and period/
prefix), on a composed name over 64 bytes with a clearer per-field
message, and on a digit count outside 6-8 or a non-positive period.
This commit is contained in:
2026-07-08 13:49:48 -07:00
parent 88a600ba66
commit cd705a446b
4 changed files with 73 additions and 9 deletions

View File

@@ -180,9 +180,38 @@ def validate_input(creds):
else:
seen[cid] = c.source
# name length
if len(cid.encode("utf-8")) > MAX_NAME:
problems.append(f"Name too long (>{MAX_NAME} bytes): '{cid}' ({c.source}).")
# invalid characters in issuer/account. The applet stores raw bytes, but
# control characters and the OATH name separators (':' / 'period/')
# corrupt display and round-trip in authenticator apps.
for fname, fval in (("issuer", c.issuer), ("account", c.account)):
ctrl = next((ch for ch in fval if ord(ch) < 0x20 or ord(ch) == 0x7F), None)
if ctrl is not None:
problems.append(
f"Control character (0x{ord(ctrl):02X}) in {fname} for "
f"'{cid}' ({c.source}).")
if ":" in fval:
problems.append(
f"Character ':' in {fname} for '{cid}' ({c.source}); it "
f"collides with the issuer:account separator.")
if "/" in fval:
problems.append(
f"Character '/' in {fname} for '{cid}' ({c.source}); it "
f"collides with the 'period/' name prefix.")
# composed name length (the applet's hard 64-byte limit)
name_bytes = len(cid.encode("utf-8"))
if name_bytes > MAX_NAME:
problems.append(
f"Composed name too long ({name_bytes}>{MAX_NAME} bytes): "
f"'{cid}' ({c.source}); shorten the issuer or account.")
# digits / period sanity
if c.digits not in (6, 7, 8):
problems.append(
f"Unusual digit count {c.digits} for '{cid}' ({c.source}); "
f"OATH codes are 6-8 digits.")
if not isinstance(c.period, int) or c.period <= 0:
problems.append(f"Invalid period {c.period!r} for '{cid}' ({c.source}).")
# algorithm supported by the applet
if c.algorithm not in (HMAC_SHA1, HMAC_SHA256):