Repository navigation
Description
Problem
The blob-name plain-text renderers in pkgs/id print stored names raw in a tab-separated, one-row-per-item format. A blob name containing a newline forges extra rows, and a tab forges extra columns — the same row-forgery class fixed for claim/doc/group in #1611 (merged in #1703) and filed separately for peers (#1613) and meta-tags (#1735), but never filed for the blob list/find renderers.
Evidence (verified this run on main 0e245370)
- Name storage accepts the characters:
src/fileops.rs:170-178—FileOps::putrejects only a name that is empty after trim; a name containing\nor\tis stored as the blob's tag verbatim. - Server pass-through:
src/protocol.rs:556-562— theMetaRequest::Listhandler copies each tag name withString::from_utf8_lossyintoMetaResponse::Listitems with no escaping or validation. id listrenders raw in all three modes:src/commands/list.rs:91(via local serve):println!("{hash}\t{name}")src/commands/list.rs:110(direct store):println!("{}\t{}", item.hash, name)src/commands/list.rs:157(remote node):println!("{hash}\t{name}")— this is the attacker-controlled case: listing a remote node's store prints that node's names.
- REPL list renders raw the same way:
src/commands/repl.rs:439(serve) andsrc/commands/repl.rs:452(local). id findrenders raw:src/helpers.rs:158-166(print_match_cli),src/helpers.rs:190-220(print_matches_cli, including the[query]group header), andsrc/helpers.rs:246-257(print_match_repl) interpolatem.name(and the query) with\tseparators and no quoting. Find matches against a remote node carry that node's names.
So a node that stores a blob under the name x\n<64-hex>\tfake.txt makes every client that runs id list <that-node> or a remote id find print a forged row that is indistinguishable from a real <hash>\t<name> row — a script parsing the output (the documented format, src/commands/list.rs:8-15) reads a hash/name pair the listing node never had.
Why it matters
id list / id find output is the machine-readable surface other tools and scripts parse. Row forgery by a remote (or merely careless) naming node corrupts inventories, can point a downstream id get at a name/hash pairing that does not exist on the listing node, and breaks the one-row-per-item invariant the rest of the fleet's plain-text output now guarantees after #1611/#1703.
Acceptance criteria
- A blob name containing
\n,\r,\t, or other control characters renders inid list(CLI serve/store/remote modes and the REPL list) as exactly one physical line with the name unambiguously delimited (e.g. the JSON-string quoting rule id(claim/doc/group): plain-text output renders names/values raw — a newline in a tag name or value forges extra rows and duplicate author=/event= fields #1611/id: quote whitespace/control values in plain-text renderer so rows cannot be forged (#1611) #1703 adopted forcommands/group.rstext()), and a tab in a name cannot create a second column. - The same quoting applies to names (and the query header) in
print_match_cli,print_matches_cli, andprint_match_replused byid findand the REPL. - Names without whitespace/control characters render byte-identically to today, and any
--jsonoutput is unchanged. - Regression tests at the renderer level: a name with an embedded newline + forged
<hash>\t<name>payload produces one line whose only unquoted hash column is the real one; a tabbed name stays one column. - Existing id crate tests still pass (laptop or CI).
Dedup
Searched open + recently closed issues for list/find/helpers TSV, newline and forgery terms. #1611/#1703 covered only the shared tokens() renderer in claim/doc/group; #1613 is peers/gossip; #1735 is commands/tag.rs meta-tag rows. The blob-name renderers in commands/list.rs, commands/repl.rs, and helpers.rs are not covered by any of them.
Activity
developing-today commentedon Oct 28, 2026
CODE-703
dezren39 commentedon Oct 28, 2026
🤖 Agent note · filed
commands/list.rs,commands/repl.rsandhelpers.rsprint stored/remote names raw; quoting fix queued for triage.repo-health-code-pilot(repo-task/v1, dedupe keyrepo-health:v1:triage:developing-today/code:issue:1742)18 remaining items
dezren39 commentedon Nov 1, 2026
Picked up by lane E worker shift — resuming task 4792. Branch
queue/4792-1742-quote-blob-renderingatb46c0d536is pushed; no PR yet. Previous shift parked because targeted cargo tests failed to compile on pre-existing errors inpkgs/id/src/repl/runner.rs(a file this branch does not modify). This shift revalidates that baseline blocker against current main, then runs the focused tests and opens the PR.test(id): take the multi-item put path in the list quoting test — a l…
Merge pull request #3763 from developing-today/queue/4792-1742-quote-…
dezren39 commentedon Nov 1, 2026
Done — task 4792 complete. PR #3763 merged (
dee3cb93d); issue #1742 closed.Verification:
pkgs/idlib suite 1674 passed. The new CLI quoting test initially failed on its own setup — a lonepath:nameput argument is auto-detected as a stdin blob name, soparse_put_specnever ran — fixed the test to take the multi-item put path; it now passes and genuinely covers the quoting. One unrelated failure recorded:serve_tests::test_world_module_shared_over_iroh_and_web_presentationstimes out on server startup (30s) under current laptop load; it does not touch this diff's surface (list/find output quoting).