Repository navigation
Description
Summary
pkgs/id petnames exist so a contact can never be mistaken for someone else: the module doc (src/petname.rs:1-7) says the store "refuses a petname that could be taken for someone else's name", and normalize_name promises a petname "cannot contain control characters". Both promises are defeated by Unicode format characters (general category Cf):
normalize_name(src/petname.rs:217-230) refuses a name whenname.chars().any(char::is_control). Rust'schar::is_controlis category Cc only. Category-Cf characters — U+202E RIGHT-TO-LEFT OVERRIDE, U+200B ZERO WIDTH SPACE, U+2060 WORD JOINER, U+FEFF, U+00AD SOFT HYPHEN — pass.set()'s confusability checks (src/petname.rs:107-135) comparefold()(lowercase) andskeleton()(look-alike map +rn→m,vv→w) verbatim. Neither strips Cf characters, so an invisible suffix changes both strings while changing nothing on screen.
Verified shapes (main d39e6111, src/petname.rs sha256 ff6cba65)
Premise exercised this run with a faithful mirror of normalize_name/fold/skeleton/set (probe folder: probe-2026-10-28-laneB-petnamecf/premise.py + premise.log):
- CTRL:
Pay<TAB>Pal→ refused by normalize (Cc).PayPa1vs allegedPayPal→ refused as confusable. Both guards work for the shapes they cover. - BUG-A (spoof): petname
PayPal+ U+200B for a different contact, while another contact's alleged name isPayPal→ ACCEPTED (foldunequal,skeletonunequal). The two names render identically in the directory view (src/directory_view.rsbuilds its labels frompetname::labels), so the viewer sees two contacts apparently named exactly "PayPal" — precisely the impersonation shape the module exists to prevent. - BUG-B (bidi): a petname containing U+202E is ACCEPTED by
normalize_name, despite its documented "cannot contain control characters" rule. A right-to-left override in a displayed label (Label::text,src/petname.rs:285-295) can reorder the visible text around it. - BUG-C:
PayPal+ U+2060 WORD JOINER → also ACCEPTED against allegedPayPal.
Existing tests (src/petname.rs mod tests) pin the Cc tab case and the PayPa1 skeleton case, but no Cf character appears anywhere in the file's tests.
Disclosure
The prepared Rust probe (probe_laneb_test.rs, same probe folder: asserts Cf names are refused by normalize_name and that the BUG-A spoof is refused by set()) was NOT executed this run — the laptop is unreachable (ongoing fleet-wide SSH outage) and verification probes are never compiled on the sandbox. Triage: run the probe first; it should currently fail on the BUG asserts. The line trace and the premise mirror above are the evidence this filing rests on.
Why it matters
Petnames are a trust surface: they are how a viewer tells contacts apart when account names are self-asserted ("Account names are alleged"). An invisible-character spoof defeats the one check standing between an alleged name and a look-alike petname, and a bidi override in a label can misrender who is who. The fix is small and local to petname.rs.
Acceptance criteria
-
normalize_namerefuses (or strips, with the choice pinned and documented) Unicode format characters (category Cf) — at minimum U+200B, U+202E/U+202A–U+202E, U+2060, U+FEFF, U+00AD — so the "no control characters" promise matches what callers and readers believe it says. - The confusability comparison in
set()cannot be bypassed by invisible characters:PayPal+ZWSP against allegedPayPalis refused (by the normalize refusal, an explicit Cf-strip infold/skeleton, or both — triage pins which layer owns it). - Tests cover at least: ZWSP suffix spoof vs an alleged name, RLO inside a name, and a Cf character against an existing petname (not only against alleged names).
- Existing petname tests still pass; legitimate names with ordinary punctuation/spaces are unaffected.
Related but distinct: #1499 (torn final line in petnames.jsonl) is the same file's crash-recovery defect — different function, different fix.
Activity
dezren39 commentedon Oct 28, 2026
agent-note:v1
agent: new-issue-creator-code-b
state: filed
Found via: filed this run (2026-10-28 lane-B creator run) after a line-by-line trace of src/petname.rs on main d39e611 (sha256 ff6cba65) and a premise exercise mirroring normalize_name/fold/skeleton/set (hidden_files/probe-2026-10-28-laneB-petnamecf/premise.py + premise.log): Cc controls and the pinned PayPa1 skeleton case are refused (controls), while PayPal+U+200B against alleged PayPal, PayPal+U+2060, and a name containing U+202E are all ACCEPTED. Disclosure: the prepared Rust probe (probe_laneb_test.go, same folder) was NOT executed — the laptop is unreachable this run (ongoing outage) and probes are never compiled on the sandbox; triage should run the probe first and narrow this issue if any verdict differs. Deliberately NOT self-solved: whether Cf handling lives in normalize_name, in fold/skeleton, or both is a small contract pin for queue-lane triage, and the laptop worktree + PR + CI cycle is unavailable while the laptop is unreachable. Distinct from #1499 (same file, torn-journal recovery — different function and fix).
34 remaining items
dezren39 commentedon Oct 31, 2026
🤖 Agent note · parked
dezren39 commentedon Oct 31, 2026
agent-note:v1 — merging (queue task 4240, lane A resume)
Attribution for dispatch run 41204856358 (head 5176ab3) is complete:
a_format_character_spoof_is_refused_by_setanda_stored_format_character_name_cannot_spoof_via_comparison(test-unit: 1989 passed on this branch vs 1984 on main — the +5 are this PR's tests).Merging under the standing rule (checks red only on the verified pre-existing set; the change itself is verified sound). Full-tier job still queued behind repo saturation (#2605); on main it fails on the same set, so it is not a differentiator.
dezren39 commentedon Oct 31, 2026
agent-note:v1 — done (queue task 4240): PR #3058 merged facf648; issue auto-closed. CI attribution and evidence in the merge note above; queue/backlog/sweep.log updated.