Un controllo della larghezza ha corrotto un Kanji

Un nome è stato inserito in una tabella terminale ed è uscito corrotto. Il cognome era 𠮷田.

Il primo carattere non è il comune 吉. È 𠮷 (U+20BB7). Si tratta di una forma rara utilizzata nei veri cognomi giapponesi. La tabella ha troncato la cella per adattarla a una colonna. Invece di un nome, ha stampato un carattere corrotto. Il kanji è stato diviso a metà.

Il bug risiedeva in una scorciatoia di una sola riga. Il codice decideva che una stringa era sicura da tagliare tramite indice prima di troncarla effettivamente. Questa logica è fallita a causa del modo in cui JavaScript gestisce le stringhe.

Una stringa JavaScript ha tre lunghezze diverse:

  • Lunghezza delle unità di codice: "𠮷".length è 2. Questo conta le unità UTF-16.
  • Conteggio dei punti di codice: [..."𠮷"].length è 1. Questo conta i caratteri effettivi.
  • Larghezza di visualizzazione: il numero di colonne occupate in un terminale è 2.

Per il testo semplice in inglese, questi numeri coincidono. "abc" ha 3 unità, 3 punti e 3 colonne. La maggior parte del codice assume che questa coincidenza sia una regola.

Il carattere 𠮷 infrange quella regola. Ha 2 unità di codice e 2 colonne. I numeri corrispondono, ma per motivi diversi. Il codice ha visto che 2 è uguale a 2 e ha utilizzato un percorso rapido per tagliare la stringa tramite indice.

Quando ha tagliato la stringa all'indice 3, ha preso il primo carattere completo e solo metà del secondo. Ciò ha lasciato un surrogato isolato. I terminali lo mostrano come un quadratino corrotto.

I caratteri giapponesi comuni come 漢 sono sicuri. Hanno 1 unità di codice e 2 colonne. Poiché 1 non è uguale a 2, il codice evita la scorciatoia errata. Il bug colpisce solo caratteri rari ed emoji.

Per risolvere il problema, è necessario:

  • Proteggere il percorso rapido per rifiutare le stringhe con surrogati alti.
  • Troncare per interi punti di codice invece che per unità di codice.

L'uso di Array.from(str) risolve il problema perché itera per punto di codice. Tratta il carattere come un'unica unità intera.

La lezione è semplice: non misurare con un'unità e tagliare con un'altra. Se misuri la larghezza di visualizzazione ma tagli tramite l'indice dell'unità di codice, corromperai i dati dei tuoi utenti.

Testa il tuo codice con un carattere CJK raro o un'emoji. L'ASCII non ti mostrerà questi errori. Devi fornire l'input che il tuo codice teme.

Fonte: https://dev.to/greymothjp/a-width-check-said-the-string-was-safe-to-cut-it-split-a-kanji-in-half-4hjk

Community di apprendimento opzionale: https://greymoth-jp.github.io/cjk-failure-corpus/