Files
pad/cmd
xarmian a92af3eebc fix(attachments): drop Bidi_Control characters from attachment names and rewrite legacy rows (BUG-3153) (#1452)
* fix(attachments): drop Bidi_Control characters from attachment names and rewrite legacy rows (BUG-3153)

A name such as "x<U+202E>gvs.txt" is a .txt that every bidi-aware renderer
shows as "xtxt.svg". About forty web read sites, the CLI list and the server
render the stored name, so the fix is at the source rather than per renderer.

DroppedFilenameRune now also drops the Unicode Bidi_Control property: U+202A to
U+202E, U+2066 to U+2069, and the marks U+200E, U+200F and U+061C. It is chosen
by property, so ZWJ (Join_Control) and emoji sequences are untouched. The rule
is shared, so both ingest doors store the name clean and the download header
drops the characters for any row. One of these characters inside an extension
also hid that extension from the blocklist, as a control byte did: an SVG
uploaded as "x.s<U+202E>vg" was accepted, and is now refused as ".svg".

Legacy rows are rewritten once, at startup, by
BackfillBidiAttachmentFilenames, and only rows containing a Bidi_Control
character are touched. The new name is the SERVED name (ServedFilename), not a
plain strip, so a legacy "x.s<U+202E>vg" becomes "x.bin" rather than storing the
blocked "x.svg". The pass is idempotent with no marker, since a rewritten row no
longer matches. Each UPDATE is conditional on the old name. Every rewrite is
logged with its old and new names quoted to ASCII, so the rewrite is reversible,
and the count is logged on every boot, 0 included.

Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX

* fix(store): the bidi filename backfill states its wider rewrite and is tested per rune on both dialects (BUG-3153, codex round 1)

- The rewrite is the served name, so a selected row also loses whatever else
  NormalizeFilename removes from a legacy name. That is now said in the
  comment and pinned by a case, and rows without a Bidi_Control are still
  never touched.
- "Reversible by the log" now says: for as long as the log is retained, with
  no durable record in the database.
- A row that gains such a name later (derived from a parent a failed pass
  left) is picked up by the next boot, since selection is by content.
- New test: one row per member of unicode.Bidi_Control, each selected by the
  SQL and rewritten. It enumerates the property itself rather than the list
  under test, so a rune missing from the LIKE list fails it.

Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX

* fix(store): a rows-affected error stops the bidi backfill instead of passing as a concurrent rename (BUG-3153, codex round 2)

The UPDATE may have landed, and without a count the row could not be logged as
rewritten, which the reversibility rests on. The comment also names the
readers it means, instead of an unmeasured count.

Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX

* docs(attachments): NormalizeFilename covers the caller-supplied doors, and derived rows inherit (BUG-3153, codex round 3)

"Every door that writes attachments.filename calls it" was not true of
thumbnails, transforms or copies, which derive their name from another row.

Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX

* test(e2e): the hostile-name parity spec expects the stored name, with Bidi_Control dropped (BUG-3153)

The spec uploads "start <U+202E>reversed<U+202C> مرحبا.png" and asserted that
the tile and dialog carry it exactly as uploaded. Ingest now drops the
override. The spec still uploads the hostile name, and now asserts that the
STORED name (RTL letters kept, controls gone) is addressable and names its
dialog. Its fixture now uses \u escapes instead of literal controls. CI caught
this on #1452; the local sweep had not searched web/e2e.

Against this tree's binary the spec passes, and against a binary without the
fix it fails, so the spec discriminates.

Claude-Session: https://claude.ai/code/session_01Vf8uZmYdsrQqJtbAmjUFSX
2026-09-23 04:35:58 -04:00
..