Document Workflow

Inspect zero-width characters before removing them from text

Preserve the source, identify six zero-width Unicode controls by code point and position, protect meaningful joiners, remove confirmed types, and validate exact output.

Written and tested by Published: Reviewed:

How this workflow was checked

Verification started with the guide's “Remove common separators while retaining an emoji joiner” fixture in Inspect and Remove Zero-Width Characters Safely. The run followed “Preserve the exact source and provenance” through “Classify each control by its possible effect”, compared the produced result to the documented expectation, and checked the distinct limits behind “Replacing the source before reviewing positions” and “Deleting ZWJ or ZWNJ as harmless whitespace”.

The ordinary zero-width separators were removed from the address and legacy text, while the ZWJ inside 👩‍💻 remained so the emoji sequence did not split.

Problem

A zero-width code point can make an email address, account key, source identifier, URL label, or copied paragraph differ from what a reviewer sees. Some occurrences are accidental residues from editors, exports, or copy-and-paste, while others are intentional: ZWJ can form one emoji or affect complex-script shaping, ZWNJ can prevent joining, ZERO WIDTH SPACE can add a break opportunity, and WORD JOINER can suppress one. Deleting every invisible character can therefore fix a comparison while corrupting language, rendering, or line layout. A defensible cleanup keeps the original, identifies exact code points and positions, removes only a confirmed set, preserves unrelated bytes in the decoded string, and validates the destination behavior separately from normalization or encoding repair.

When to use this

  • Two visually identical usernames, email addresses, search terms, or database keys compare as different values.
  • Pasted source code, CSV data, environment values, or identifiers fail parsing or validation without a visible cause.
  • Copied text wraps at an unexpected position or refuses to wrap where the visible spacing suggests it should.
  • An emoji or Arabic, Persian, Indic, or Mongolian string contains a joining control that must be reviewed rather than deleted blindly.
  • A suspicious string needs code-point evidence for human triage without treating a hidden control as proof of malicious intent.

Steps

  1. Step 1

    Preserve the exact source and provenance

    Keep the original file, clipboard value, database field, or message unchanged. Record where it came from, how it was decoded, and the comparison or rendering symptom. A screenshot alone is not a source because it cannot preserve invisible code points.

  2. Step 2

    Separate text inspection from byte encoding

    Decide whether the evidence is a decoded string or an original file. A leading U+FEFF in browser text can be reported, but only the original bytes can prove a UTF-8, UTF-16, or UTF-32 signature. Keep byte-level BOM work in a separate step.

  3. Step 3

    Run an inspection without removal

    Choose Inspect only first. Review total detections, affected lines, code-point types, one-based positions, line and column values, and escaped context. A zero result means none of the six supported controls were found, not that the text has no invisible Unicode characters.

  4. Step 4

    Classify each control by its possible effect

    Treat U+200B and U+FEFF as common cleanup candidates, then examine the source before changing them. Protect ZWNJ, ZWJ, WORD JOINER, and MVS unless the destination contract proves they are unwanted; they can affect shaping, emoji sequences, line breaking, or Mongolian text.

  5. Step 5

    Remove the narrowest confirmed set

    Use Common cleanup for U+200B and U+FEFF, or Choose types for a smaller or explicitly reviewed set. Use All supported only when the source owner and destination rules allow the resulting changes to joining and wrapping.

  6. Step 6

    Compare exact output and change counts

    Confirm detected equals removed plus preserved, inspect the first changed contexts, and compare the output with the untouched source. Verify LF, CRLF, CR, tabs, spacing, terminal line endings, emoji, and multilingual shaping before running normalization, trimming, or other cleanup.

  7. Step 7

    Validate the destination and record the decision

    Test a non-sensitive copy in the database, identity provider, parser, search index, editor, URL handler, or publishing system that will consume it. Record which code points were removed and why, then retain the original when auditability or rollback matters.

Example

Remove common separators while retaining an emoji joiner

Input

account​@example.com
Emoji: 👩‍💻
legacymark

Output

account@example.com
Emoji: 👩‍💻
legacymark

Common mistakes

Replacing the source before reviewing positions

Once a hidden control is removed, its location and surrounding evidence may be lost. Preserve the original and capture the inspection result before accepting output.

Deleting ZWJ or ZWNJ as harmless whitespace

These are format controls, not ordinary spaces. Their removal can split emoji or alter cursive and Indic shaping, so they require language and destination context.

Calling a decoded U+FEFF a verified file BOM

The browser string no longer contains the original encoding signature bytes. Inspect the original file separately when byte order or signature evidence matters.

Normalizing and removing in one unreviewed pass

NFC, NFKC, case folding, trimming, and line-ending conversion can change other code points. Separate the stages so a comparison reveals which operation caused each difference.

Treating a clean scan as a safety certificate

The detector covers six named controls, not every Unicode security concern. Homoglyphs, bidirectional controls, unsafe links, parser behavior, and malicious code require their own validation.

FAQ

Why should inspection happen before cleanup?

It preserves evidence about type, position, line, column, and context. That evidence helps distinguish an accidental separator from a meaningful joiner and supports a smaller, reviewable change.

Which characters are protected by Common cleanup?

It preserves U+200C ZWNJ, U+200D ZWJ, U+2060 WORD JOINER, and U+180E MVS. It removes U+200B and U+FEFF, but those changes should still be compared with the source because they can affect line breaking.

Can I remove ZWJ from source code but keep it in emoji?

Use separate, clearly scoped inputs or remove by confirmed position in a code-aware workflow. A type-wide selection removes every U+200D in the submitted text and does not infer whether each occurrence is code, language, or emoji.

Does this workflow detect homoglyphs or bidirectional controls?

No. The fixed scope is six zero-width controls. Confusable characters, bidi controls, variation selectors, soft hyphens, and other default-ignorable code points need dedicated Unicode security review.

Will removal change normalization or CRLF?

The engine omits selected code points only. It does not normalize text or rewrite LF, CRLF, CR, tabs, spacing, or a final line ending. A later editor or destination may still do so.

Can confidential text be inspected safely?

Tool processing remains local in the browser and does not send the input to a conversion service, but authorization, browser extensions, device controls, and the destination still matter. Use redacted evidence when policy requires it.