Repository navigation
Description
Problem
The plain-text output of id world list prints each world name raw in a tab-separated, one-row-per-world format. The names come from the host's worlds reply over the wire and are accepted by the client with no validation, so a name containing a newline forges an extra row and a tab forges extra columns — the same row-forgery class fixed for peers in #1613 (plain_field, commands/text.rs) and filed for blob names (#1742) and meta-tags (#1735), but never filed for the world-directory renderer, which uses neither plain_field nor the tokens() renderer.
Evidence (verified this run on main 33b6cb36)
- Rendering:
pkgs/id/src/commands/world.rs:677-688— the non-JSON branch printsprintln!("{name}{default_marker}\t{}", if open { "open" } else { "closed" })withnameinterpolated verbatim. - Wire receipt accepts anything:
pkgs/id/src/world_net.rs:690-697—WorldClient::list_worldscollectsw["name"].as_str()from the host's JSON reply with no validation or re-check againstvalidate_world_name. - A conforming current hub does validate at create time (
pkgs/id/src/world_hub.rs:48-63,a-z0-9-_only), so the payload requires a non-conforming, older, compromised, or hand-provisioned host/storage — but the ruling recorded incommands/text.rs:1-17(id(peers): a peer's announced name is printed raw — a newline in a gossip announcement forges extra TSV rows in id peers and the REPL #1613) is that the contract lives at the printer: a printer never lets a value break row structure, precisely because a store/host may already hold a value ingest validation could not have cleaned. The--jsonbranch (world.rs:668-676, viadirectory_json) is safe. - Probe (verbatim replication of the format string, python; output in the goal's
hidden_files/probe-2026-10-31-laneB-worldlist/probe.txt): worldslobby,evil\nfake\topen,a\tbrender as 4 physical lines for 3 worlds — the second line is a fully-formed forged rowfake\topen— with tab-field counts[2, 1, 3, 3]instead of 2 per row.
Why it matters
id world list plain output is the operator/script surface for the world directory (the JSON shape is the documented machine feed, #812; the plain rows are what terminals and shell pipelines consume). A hostile or merely buggy host can plant a phantom "open" world row in every listing a client prints, or shift the open/closed column — the exact output-forgery pattern #1611/#1613/#1742/#1735 eliminate renderer by renderer. This is the remaining unguarded TSV renderer in commands/.
Acceptance criteria
- A world name (and the default-world marker comparison) in the plain branch of
id world listrenders throughplain_field(commands/text.rs) or an equivalent quoting rule: a name containing\n,\r,\t, or other control characters produces exactly one physical line with exactly 2 tab fields. - Names with no structure-breaking character (including ordinary valid world names) render byte-identically to today; the
--jsonoutput is unchanged. - Regression tests at the renderer level, mirroring the
commands/text.rs/ peers tests: a name with an embedded newline + forged<name>\topenpayload produces one line; a tabbed name stays one field. - Existing id crate tests still pass (laptop or CI).
Dedup
Searched open + recently closed issues for world-list / TSV / forgery / plain_field / validate_world_name terms. #1613 is peers/gossip, #1742 is list/find blob names, #1735 is meta-tags, #2839 is a plain_field trigger-set gap for peers, #2960/#2890 are journal-restore display-name/scope validation in the world engine. None covers the commands/world.rs world-directory plain renderer or the world_net.rs receipt path.
Activity
dezren39 commentedon Oct 31, 2026
🤖 Agent note · filed
id world listplain output renders host-supplied world names raw — a newline in a name forges an extra TSV row and a tab forges columns; the client accepts names from the host's worlds reply with no validation (world_net.rs), and this renderer uses neither plain_field nor tokens(). Verified this run by code read on main 33b6cb3 plus a verbatim format-string replication (3 worlds → 4 physical lines, forged 'fake⇥open' row).dezren39 commentedon Nov 1, 2026
Adopted as Phase 3a of design #3455 (a value cannot act on the terminal — one format-character contract for pkgs/id plain output; design doc
files/designs/code-id-plain-output-rendering.mdin the keep-the-four-repos-healthy goal workspace). Triage: do NOT re-file, re-scope or split this issue — record the phase structure instead.This phase is NOT gated: routing the world name through
plain_fieldis correct today for structure characters (newline/tab forgery, this issue's acceptance), and its format-character (Cf) coverage arrives automatically when Phase 1 (#2839) widensplain_field's predicate — no change needed in this issue when that lands. The default-marker comparison stays on the raw name;--jsonunchanged.10 remaining items
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done / handed off
dezren39 commentedon Nov 1, 2026
🤖 Agent note · picked up
fix(id): render world names through plain_field in world list plain o…
dezren39 commentedon Nov 1, 2026
🤖 Agent note · done
cargo test world_list_plain_row3/3 pass,cargo test commands::text8/8 pass; Codex review completed, no findings.