Skip to content
View watermark-check's full-sized avatar

Block or report watermark-check

Block user

Prevent this user from interacting with your repositories and sending you notifications. Learn more about blocking users.

You must be logged in to block users.

Content in all repositories owned by your account will be closed.
Maximum 250 characters. Please don’t include any personal information such as legal names or email addresses. Markdown is supported. This note will only be visible to you.
Report abuse

Contact GitHub support about this user’s behavior. Learn more about reporting abuse.

Report abuse
watermark-check/README.md

WatermarkCheck

Browser-based checker and cleaner for invisible Unicode characters in text and code.

Paste text or code, see every hidden character exposed in an x-ray view — zero-width spaces, bidirectional controls, tag characters, non-standard whitespace — and copy a clean version. Everything runs locally in the browser: no upload, no sign-up, no logging.

What it does

  • Detects 64 classes of invisible Unicode characters and labels each one in place
  • Cleans text and code contextually, without breaking emoji, Persian, Hindi or other scripts that legitimately need zero-width characters
  • Flags bidirectional controls that enable Trojan Source attacks (CVE-2021-42574)
  • Makes no network request while you type; keeps working offline

What it does not do

It cannot remove statistical watermarks such as SynthID or token-selection marks — those live in word choice, not in characters, and no character-level tool can touch them.

This repository

The detection engine, as a dependency-free ES module. It is the same code that runs in the browser on the hosted version.

src/engine.js        the engine: lookup, contextual decisions, cleaning
src/chars.js         the character table it runs on (64 entries)
src/index.js         public API
bin/wmc.js           command-line interface
test/engine.test.js  test suite (no framework, no dependencies)
test/fixtures/       expected output captured from the deployed build
examples/demo.js     runnable demonstration
docs/API.md          full API documentation
import { clean, analyze } from './src/index.js';

clean('he​llo world');   // U+200B inside the word -> 'hello world'
clean('م‌ر');            // U+200C in Arabic -> unchanged, the script requires it
clean('x‌y');            // the same U+200C in Latin -> 'xy'

Run the tests:

node --test test/engine.test.js

Node 18 or newer. No install step, no dependencies.

Why the contextual part matters

Blind removal corrupts real text. A zero-width joiner holds family emoji together and is required in Devanagari. A zero-width non-joiner is required in Persian. A blank braille cell is braille content. Tag characters are a hidden payload — except inside a flag emoji, where they are the flag. Each of those decisions needs the surrounding characters, which is why decide() in src/engine.js is as long as it is.

Some invisible characters belong to the script they would be tested against: U+3164 is itself Script=Hangul, U+17B4 is Script=Khmer. Without an explicit exclusion a run of padding characters would vouch for itself and survive cleaning, which is exactly the trick they get used for. See SELF_WITNESS in src/engine.js.

Data

The character dataset is published separately as JSON under CC BY 4.0: invisible-unicode-characters

Links

Hosted version: watermarkcheck.com

Licence

MIT — see LICENSE.

Maintained by Oleh Vozniak.

Popular repositories Loading

  1. watermark-check watermark-check Public

    Detect and remove invisible Unicode characters from text and code, without damaging emoji, Arabic, Korean, Khmer or braille. The engine behind watermarkcheck.com. No dependencies.

    JavaScript

  2. invisible-unicode-characters invisible-unicode-characters Public

    Complete list of invisible Unicode characters as JSON: zero-width spaces, bidi controls, tag characters, variation selectors — with risk levels and recommended handling. Powers watermarkcheck.com