glsec

Rules / GL078

GL078 Invisible / bidirectional Unicode characters (Trojan Source)

Severity: warn
OWASP: CICD-SEC-4 – Poisoned Pipeline Execution

Risk

.gitlab-ci.yml files can carry invisible or bidirectional Unicode control characters that make the rendered text differ from what is actually parsed and executed — the "Trojan Source" class (CVE-2021-42574). A script: line can look benign in a reviewer's editor while running something else, or hide payload via zero-width characters that survive copy-paste.

What glsec checks

GL078 scans the raw bytes of the file (not the parsed YAML tree, where these characters are invisible) and flags any occurrence of:

Bidirectional control characters (reordering attacks):

Range Characters
U+202A–U+202E LRE, RLE, PDF, LRO, RLO
U+2066–U+2069 LRI, RLI, FSI, PDI
U+061C Arabic letter mark

Zero-width / invisible characters (hidden-payload / homoglyph smuggling):

Code point Character
U+200B–U+200D Zero-width space / non-joiner / joiner
U+2060 Word joiner
U+FEFF Zero-width no-break space / BOM
U+00AD Soft hyphen

A single leading U+FEFF byte-order mark is allowed so BOM-prefixed files are not flagged; any later U+FEFF is reported.

Line breaks that only YAML sees:

Code point Character
U+000D Carriage return not followed by a line feed (CRLF line endings are fine)
U+0085 Next line (NEL)
U+2028 Line separator
U+2029 Paragraph separator

The YAML parser starts a new line at each of these, but most editors, git diff and GitLab's file view don't. A file containing them is parsed into different lines than a reviewer reads, and the line numbers of findings after them, from glsec and from other tools, point at the wrong place. GL078 reports them with the line and column as an editor counts them (by line feeds).

Trigger example

A line that renders as a harmless comment but reorders under a U+202E (RLO) override, or a script: value with an embedded U+200B, produces a finding pointing at the line and column of the offending code point:

GL078 warn: invisible/bidirectional Unicode character U+202E (RIGHT-TO-LEFT OVERRIDE) at column 18 …

Safe alternative

Remove the control character, and use plain \n or \r\n line endings. CI configuration never needs bidirectional, zero-width or Unicode line-break characters; keep .gitlab-ci.yml to plain ASCII (plus ordinary accented text where genuinely required).

False-positive risk

Very low — these code points essentially never appear legitimately in a CI configuration.

Documentation for glsec 1.23.0. Source of this page · Rule implementation