Ir al contenido
Ghostchars

Cómo decide Ghostchars qué eliminar

Una pasada sobre el texto, una decisión por carácter y un motivo legible para cada decisión. Sin modelo, sin heurísticas, sin puntuación.

Cuatro pasadas

La limpieza se ejecuta en un orden fijo. Solo la tercera pasada mira los caracteres uno a uno; las dos primeras existen para que la tercera vea una cadena predecible.

  1. Normalizar (opcional)

    Si lo has pedido, el texto se normaliza primero a NFC o a NFKC. Esto es lo que derrota a la esteganografía por forma de normalización, en la que el mensaje se codifica en la elección entre la «é» como un punto de código y la «e» más un acento combinante. NFKC va más allá y unifica las letras matemáticas con estilo, las ligaduras y las formas de ancho completo, lo cual pierde información: por eso está desactivado por defecto.

  2. Unificar finales de línea y secuencias de espacios (opcional)

    CRLF y CR pasan a LF; la barra de cita de una terminal al principio de una línea se va con el espacio que la sigue; una raya con espacios pasa a un guion con espacios; los espacios y tabuladores del final se cortan en cada línea; las secuencias de espacios dentro de una línea se reducen a uno. Son reescrituras de patrones sobre toda la cadena, no decisiones carácter a carácter, y cada una es su propia opción porque cada una cambia texto que puedes ver.

  3. Clasificar cada carácter

    Cada carácter se busca en las tablas del motor en un orden fijo, y gana la primera coincidencia. El resultado es una categoría, la opción que la gobierna y aquello por lo que debe sustituirse: una cadena vacía para «borrar», un espacio normal para un espacio exótico, una letra ASCII para una letra parecida. Los caracteres corrientes no coinciden con nada y se devuelven intactos.

  4. Reconstruir, con contexto

    La salida se monta carácter a carácter. Un carácter cuya opción está desactivada se copia tal cual y se informa como detectado pero sin tocar. Un carácter cuya opción está activada se elimina o se sustituye. Durante todo el proceso, el motor recuerda el último carácter que CONSERVÓ de verdad, no el último que vio, porque esa es la base a la que se engancha una unión o un selector de variación, y los caracteres de unión no hacen avanzar la base.

Las cuatro pasadas van de caracteres; ninguna lee una oración. Si la escritura repite los hábitos que se notan, oraciones todas del mismo largo, conectores prefabricados, muchas rayas, esa es otra medición: el informe de estilo los cuenta y te entrega los números, sin puntaje y sin probabilidad.

El orden de decisión

Las categorías se comprueban en este orden y la primera coincidencia decide. El orden importa: un espacio de ancho cero se comprueba antes que la regla genérica de control de formato, así que se informa como carácter de ancho cero y no como un «Cf» anónimo.

#Por categoríaLo gobierna
1Ancho cero e invisiblesPasada por defecto (siempre activo)
2Anulaciones bidireccionalesPasada por defecto (siempre activo)
3Marcas bidireccionalesMarcas bidi --bidi
4Uniones, selectores y rellenosAgresivo -a
5Cualquier otro control de formatoPasada por defecto (siempre activo)
6Caracteres de uso privadoCaracteres de uso privado --pua
7Espacios exóticosEspacios -s
8Separadores de líneaFinales de línea -n
9Caracteres de controlCaracteres de control --controls
10Puntuación tipográficaPuntuación --punct
11Barras de cita de la terminalBarras de cita --gutters
12Letras parecidasLetras parecidas --confusables

Dentro de este orden hay dos excepciones. El árabe, el siríaco y el kaithi tienen controles de formato que son ortografía corriente, como los signos de número árabes y el signo de número kaithi, y se comprueban antes que la regla genérica de control de formato y se dejan en paz. El tabulador, el salto de línea y el retorno de carro quedan fuera del grupo de caracteres de control, porque borrarlos uniría líneas entre sí.

Por qué decide el contexto

Hay cuatro familias de caracteres que son invisibles Y cumplen una función: las uniones, los selectores de variación, los rellenos propios de una escritura y las marcas bidireccionales. El mismo punto de código puede ser contrabando en una posición y la ortografía correcta en otra, así que el motor mira a los vecinos antes de decidir. Esta es la parte que la mayoría de los limpiadores hace mal, en los dos sentidos: o se dejan los que están escondidos, o rompen los que hacen falta.

El emoji de familia

Un emoji de familia son tres o cuatro personas soldadas con uniones de ancho cero. Quítalas y un emoji se convierte en tres. Ghostchars conserva una unión cuyos vecinos a ambos lados son bases de emoji, y elimina ese mismo punto de código cuando está entre dos letras latinas, donde no une nada y se esconde muy bien.

La palabra persa

El persa escribe sus formas continuas y plurales con una no unión de ancho cero: no es decoración, es la ortografía. Ghostchars conserva una unión o una no unión cuando los caracteres de ambos lados pertenecen a la misma escritura ligada (árabe, persa, devanagari, malayalam) y la elimina en cualquier otro sitio. La misma regla protege las vocales del jemer, los selectores del mongol y los rellenos de jamo del hangul junto a sus propias escrituras.

La bandera de Escocia

Una bandera de subdivisión es una bandera negra seguida de caracteres tag que deletrean un código de región y de un tag de cancelación. Esos mismos caracteres tag, sueltos, son el ataque de ASCII smuggling: instrucciones invisibles que un modelo de lenguaje va a leer. Ghostchars analiza la secuencia entera: los caracteres tag dentro de una bandera completa se conservan, y todos los caracteres tag fuera de una se eliminan.

La frase en hebreo

Las marcas de izquierda a derecha y de derecha a izquierda son la forma que tiene el texto de dirección mixta de mantener su puntuación en el lado correcto. Ghostchars conserva una marca bidi cuyo vecino es un carácter de derecha a izquierda y elimina la que va a la deriva en un párrafo en inglés, donde su única función es llevar un bit. Los cinco controles de anulación e incrustación no reciben ese beneficio de la duda: pueden reordenar texto que tú no escribiste, así que siempre se van.

Cada una de estas decisiones se informa. Un carácter conservado aparece como «conservado: cumple una función», junto con la opción que lo eliminaría de todos modos, así que nada se decide en silencio y nada se te oculta.

Verificado contra once métodos publicados

La herramienta de línea de comandos incluye una prueba adversaria que implementa once técnicas documentadas de ocultación en texto como pares codificador/descodificador. Para cada una incrusta un secreto, limpia el texto y comprueba dos cosas: que el descodificador ya no puede recuperar el secreto, y que el texto visible de cobertura sobrevive idéntico byte a byte.

MétodoSe vence con
Zero-width binary (ZWSP / ZWNJ / ZWJ)pasada por defecto
Word-joiner and invisible-maths binarypasada por defecto
Tag-block ASCII smugglingpasada por defecto
Variation-selector byte smugglingpasada por defecto
Bidi mark binary (LRM / RLM)pasada por defecto
SNOW trailing whitespaceEspacios finales --trailing
Cyrillic homoglyph binaryLetras parecidas --confusables
NFC / NFD normalisation-form binaryNormalizar (NFC) --nfc
NBSP / space homoglyph binaryEspacios -s
Single / double space binaryUnificar espacios --collapse
Curly / straight apostrophe binaryPuntuación --punct

Cinco de los once caen solo con la pasada por defecto. Los otros seis necesitan una opción, porque el portador es un carácter que se ve, como un espacio, un apóstrofo o una letra parecida, y eliminarlos sin que lo pidas cambiaría el texto.

El mismo motor en dos sitios

El motor del navegador es un port de la herramienta de línea de comandos en Python, no una reinterpretación.

Un generador ejecuta la implementación en Python sobre un corpus de casos (todos los puntos de código listados, todos los casos de contexto, las once cargas de esteganografía, prosa mixta realista) con catorce combinaciones de opciones, y graba la salida limpia y todos los hallazgos. El motor de TypeScript se prueba contra esa grabación. Cualquier desviación entre los dos hace fallar la compilación, así que «la web hace lo mismo que la CLI» es el resultado de una prueba y no una promesa.

Eso importa por un motivo práctico: puedes comprobar un archivo en un hook de pre-commit con la CLI y obtener exactamente el mismo resultado que te habría dado la web.

El mismo motor corre en un tercer sitio. El informe del documento descomprime un .docx o un .odt y lee el XML que hay dentro en vez de una cadena pegada, así que un carácter recibe dentro de un archivo el mismo veredicto que en el campo de la portada, y los fragmentos ocultos, el control de cambios y los metadatos que lo rodean se informan sin eliminarse.

Lo que esto no puede hacer

Todo lo anterior trata de caracteres. Estas cuatro cosas no tratan de caracteres, y ninguna limpieza las toca.

  • Marcas de agua estadísticas: Métodos como SynthID-Text y el de la «lista verde» de Kirchenbauer sesgan qué PALABRAS elige un modelo. La señal vive en la elección de palabras a lo largo de un pasaje entero, no en ningún carácter. Solo reescribir el texto la cambia, y esta herramienta no reescribe texto.
  • Detección estilométrica: Detectores como GPTZero puntúan lo predecible que es cada palabra y cuánto varía esa previsibilidad. Eliminar un espacio de ancho cero no mueve esa puntuación ni un punto. El informe de hábitos de escritura mide los hábitos editables que hay detrás (la variación de la longitud de las oraciones, las palabras delatoras, los conectores prefabricados) y aun así se niega a dar una probabilidad.
  • Lo que se esconde a nivel de formato: El texto blanco sobre blanco, los fragmentos ocultos, los comentarios de HTML y los metadatos de un PDF se esconden FUERA del texto. La herramienta de documentos informa de ellos: fragmentos ocultos, control de cambios, comentarios, ids de sesión de edición y metadatos de autoría. Pero a propósito no los borra, porque borrarlos cambia el documento y esa decisión debería ser tuya.
  • Canales de menos de un bit por emoji: Una cadena de uniones entre emojis, y un selector de variación detrás de uno, se conservan por defecto porque así funcionan los emojis. Alguien empeñado en ello todavía puede esconder ahí una pequeña cantidad de datos. Activa Agresivo si necesitas que desaparezcan, y asume que tus emojis se van a deshacer.

Ghostchars no da ninguna probabilidad, ninguna puntuación ni ningún veredicto sobre quién escribió un texto. Te dice qué caracteres hay en tu cadena, dónde están y qué le pasa a cada uno.

Preguntas

¿Por qué no eliminar sin más todos los caracteres invisibles?

Porque varios de ellos cumplen una función. Eliminar todas las uniones de ancho cero convierte un emoji de familia en tres personas separadas y escribe mal las palabras persas. Eliminar todas las marcas bidi rompe la composición del hebreo mezclado con inglés. Un limpiador que hace esto en silencio es peor que no tener limpiador, porque no puedes ver lo que se llevó.

¿La versión del navegador es igual que la herramienta de línea de comandos?

Sí, y está probado que lo es. Unos casos de referencia generados desde la implementación en Python fijan el motor de TypeScript a una salida idéntica byte a byte para cada caso y cada combinación de opciones; la compilación falla ante cualquier diferencia.

¿Puede decirme si un texto lo escribió una IA?

No, y nunca va a pretenderlo. Los caracteres ocultos te dicen que un texto pasó por algún programa. No te dicen por cuál, y no te dicen quién escribió las palabras. Cualquier herramienta que convierta un recuento de caracteres en un porcentaje de confianza se está inventando el número.

¿Mi texto sale de mi navegador?

No. No hay procesamiento en el servidor ni nada a lo que subirlo: el motor es JavaScript ejecutándose en tu dispositivo, y el sitio es un conjunto de archivos estáticos. Puedes desconectarte de la red y sigue funcionando.

¿Estadísticas de uso anónimas?

Ghostchars procesa tu texto en el navegador y nunca lo envía a ninguna parte. Nos gustaría contar las visitas con un servicio de analítica alojado por nosotros y con un uso mínimo de cookies. El contenido, nunca.