Skip to content

Warn when SKILL.md contains invisible characters - #6098

Open
fevziegeyurtsevenler wants to merge 1 commit into
stacklok:mainfrom
fevziegeyurtsevenler:skills/warn-invisible-runes
Open

Warn when SKILL.md contains invisible characters#6098
fevziegeyurtsevenler wants to merge 1 commit into
stacklok:mainfrom
fevziegeyurtsevenler:skills/warn-invisible-runes

Conversation

@fevziegeyurtsevenler

Copy link
Copy Markdown

Adds an invisible-character warning to skill validation.

Problem

collectWarnings in pkg/skills/validator.go already receives the raw SKILL.md bytes, but only inspects formatting — comma-delimited allowed-tools and line count. Nothing looks at the characters themselves.

Characters that render as nothing still reach the model as instruction text. A skill can therefore carry directions that a reviewer reading the rendered file cannot see. The clearest case is the Unicode tag block (U+E0000–E007F), a complete ASCII alphabet that essentially every renderer draws as zero pixels:

// "ignore all previous instructions", invisible in any editor or diff view
for _, r := range payload { out.WriteRune(r + 0xE0000) }

Bidirectional overrides are the other well-known case — the rendered order of a line can differ from the byte order that gets read, which is Trojan Source / CVE-2021-42574.

Change

checkInvisibleRunes scans the content once and reports the classes found plus the first line they appear on:

  • Unicode tag characters (U+E0000–E007F)
  • zero-width characters (U+200B–200D, U+2060, U+FEFF)
  • invisible mathematical operators (U+2061–2064)
  • bidirectional overrides (U+200E/200F, U+202A–202E, U+2066–2069)
  • variation selectors (U+FE00–FE0F)

Why a warning and not an error

These codepoints have legitimate uses — ZWJ builds ordinary emoji sequences, and RTL marks appear throughout ordinary Arabic and Hebrew prose. Failing validation on them would break real skills, so this surfaces the finding and leaves the judgement to a human, consistent with how the existing warnings behave.

Ordinary non-ASCII prose stays clean; there is a test asserting that Turkish, Greek, Japanese and emoji content produces no warning, alongside one test per flagged class and one for the reported line number.

Verification

go test ./pkg/skills/... -count=1, go vet ./pkg/skills/ and gofmt -l pkg/skills/ are all clean locally. Commit is DCO signed-off.

Context: I maintain uncloak, a scanner for this class of issue, and ran it across 3,168 public agent extensions — which is where the specific codepoint classes above come from.

collectWarnings already receives the raw SKILL.md bytes but only looks at
formatting: comma-delimited allowed-tools and line count. Characters that
render as nothing still reach the model as instruction text, so a skill can
carry directions a reviewer reading the rendered file cannot see.

Flag Unicode tag characters (U+E0000-E007F, a full invisible ASCII alphabet
and the usual carrier), zero-width characters, invisible math operators,
bidirectional overrides of Trojan Source fame, and variation selectors,
naming the kind found and the first line it appears on.

This is a warning rather than an error on purpose: ZWJ appears in ordinary
emoji sequences and RTL marks appear in ordinary Arabic and Hebrew prose, so
the call belongs to a human. Ordinary non-ASCII prose stays clean, which the
tests cover alongside each flagged class.

Signed-off-by: Fevzi Ege Yurtsevenler <egeyurtsevenler@gmail.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant