Skip to main content
interlace
Plugin: secure-codingRules

no-bidi-characters

Disallows Unicode bidirectional control characters, which let source render differently than it compiles (Trojan Source, CWE-1007)

CWE: CWE-1007 Reference: Trojan Source (Boucher & Anderson, 2021)

Bidirectional control characters are invisible. They tell a text renderer to reorder the characters around them, so an editor, a terminal, and a GitHub diff can all display one program while the compiler builds a different one. The reviewer approves what they see; the build ships what is actually there.

This is the defect class behind CVE-2021-42574, which affected essentially every language with Unicode source support.

Rule details

Reports any of the Unicode bidi control characters appearing anywhere in the source — string literals, comments, identifiers, or template contents.

Examples of incorrect code:

// The comment below contains U+202E RIGHT-TO-LEFT OVERRIDE.
// Rendered, it reads as an early return. Compiled, it is not one.
if (accessLevel !== 'user‮ ⁦// Check if admin⁩⁦') {
  grantAdmin();
}
const isAdmin = false; /*‮ } ⁦if (isAdmin)⁩ ⁦*/

Examples of correct code:

// Plain ASCII: what is displayed is what is compiled.
if (accessLevel !== 'user') {
  grantAdmin();
}
// Legitimate right-to-left TEXT needs no control characters —
// the characters carry their own direction.
const messages = { he: 'שלום', ar: 'مرحبا' };

Why this rule reports what it does

The distinction that matters is control characters versus script. Hebrew, Arabic, Persian and Urdu text is welcome and reports nothing — those characters have intrinsic directionality. What is reported is the invisible override: U+202AU+202E, U+2066U+2069, and the directional marks U+200E/U+200F.

Options

OptionTypeDefaultDescription
additionalCharactersstring[][]Extra code points to treat as bidirectional control characters
allowDirectionalMarksbooleanfalsePermit U+200E / U+200F (LRM / RLM), which cannot reorder code
{
  'secure-coding/no-bidi-characters': ['error', {
    additionalCharacters: [],
    allowDirectionalMarks: false,
  }],
}

allowDirectionalMarks — the two directional marks are occasionally load-bearing in mixed-direction UI copy, where they fix punctuation placement in an otherwise correct string. They cannot reorder code, only adjacent characters. Set this to true if your localisation files legitimately use them; the overrides and isolates stay reported either way.

additionalCharacters — accepts code points as strings, for organisations with a stricter Unicode policy than this rule's default set.

Suggestions

The rule provides a removeBidiCharacter suggestion that deletes the offending character. It is offered rather than auto-fixed deliberately: if the character is load-bearing in a localisation string, silently removing it on --fix would corrupt the copy.

When not to use it

If your build pipeline already rejects non-ASCII source outright, this rule is redundant. Otherwise, keep it on — the cost is a single scan and the failure mode it prevents is a code review that cannot be trusted.

Did this rule catch something? Star the repo to get new CWE coverage as we ship it — or follow the AI-code-security benchmarks behind these rules.