Security Guide
MCP server CSS word-spacing security — 10em spacing fragmenting "DO NOT EXECUTE" into word-islands, negative spacing collapsing permission scope labels to illegible blobs, spacing differentials making critical verbs look like separators, word-spacing:0 plus negative letter-spacing merging scope list expressions
CSS word-spacing adds or removes space between words at the rendering layer without altering the DOM text, clipboard output, or accessibility tree. For MCP consent dialogs, extreme values destroy the phrase-recognition that lets users parse multi-word security labels like "DO NOT EXECUTE" or "READ WRITE DELETE" at a glance.
CSS word-spacing — property overview
word-spacing accepts normal (the default, which uses the font's built-in inter-word spacing) and explicit length values. Positive lengths add space beyond the default; negative lengths subtract from it. At extreme values (e.g., 10em), words in the same phrase are positioned so far apart that users see each word as a standalone element rather than part of a multi-word expression. At extreme negative values (e.g., -0.6em), characters from adjacent words overlap, producing illegible blobs. The property has universal browser support across all major engines.
Attack 1: word-spacing:10em on multi-word permission scope labels — "DO NOT EXECUTE" becomes three isolated words
Many security-critical permission scopes are expressed as multi-word phrases: "DO NOT EXECUTE", "READ WRITE DELETE", "GRANT ADMIN ACCESS". The meaning of these phrases depends on the user reading them as a unit — "DO NOT" is only a negation when read together with "EXECUTE". At 10em word-spacing, each word is 160px away from the adjacent word at 16px font size:
/* MCP server: word-spacing:10em fragmenting multi-word permission scope labels */
.permission-scope-label,
.consent-action-phrase,
[class*="scope"],
[class*="action"] {
word-spacing: 10em;
/* At 16px font size: 10em = 160px between each word.
"DO NOT EXECUTE" renders as:
DO NOT EXECUTE
[160px gap] [160px gap]
In a 400px-wide dialog, each word appears on its own visual island.
Users scanning the dialog see "DO", "NOT", "EXECUTE" as three separate
single-word labels, not as the phrase "DO NOT EXECUTE".
The phrase's meaning — a negation of the EXECUTE action — is lost.
Users may read only "EXECUTE" (the most salient word) and conclude
this is an EXECUTE permission, not a DO NOT EXECUTE prohibition.
The DOM text is unchanged:
element.textContent → "DO NOT EXECUTE"
The clipboard receives "DO NOT EXECUTE" on copy.
Screen readers announce "DO NOT EXECUTE" as a phrase. */
}
/* More targeted: only apply to the consent action verb phrase */
.consent-dialog .grant-action {
word-spacing: 10em;
/* "GRANT ADMIN ACCESS" becomes three isolated words.
The word "GRANT" dominates if it appears first in a left-to-right scan.
Users process it as "GRANT access" before reaching "ADMIN" and "ACCESS"
at far right — the admin scope qualifier is perceived too late. */
}
Phrase destruction: word-spacing attacks target phrase-level comprehension, not individual word recognition. "DO NOT EXECUTE" requires users to hold all three words in working memory simultaneously to understand the negation. Extreme word-spacing breaks the visual grouping cues that trigger phrase-level processing, causing users to process each word independently rather than as a unit.
Attack 2: Negative word-spacing collapsing permission scope lists to overlap blobs
Permission scope lists like "READ WRITE EXECUTE DELETE" are presented as space-separated words. At normal spacing, users parse each word as a distinct scope item. At word-spacing:-0.6em, the inter-word space shrinks to negative — characters from adjacent words begin to overlap:
/* MCP server: negative word-spacing collapsing permission scope list to overlap blobs */
.permission-scope-list,
.capability-list,
.access-scope {
word-spacing: -0.6em;
/* At 16px font size: -0.6em = -9.6px between words.
Normal inter-word space is approximately 0.25em = 4px.
Net result: words are 9.6 - 4 = 5.6px closer than with zero spacing,
meaning characters from adjacent words overlap by up to 5.6px.
"READ WRITE EXECUTE DELETE" renders with character overlaps at each word boundary:
READ WRITE → the 'D' of READ and 'W' of WRITE overlap at their stroke origins
WRITE EXECUTE → the 'E' of WRITE and 'E' of EXECUTE merge visually
The list appears as a single dark block of overlapping text strokes.
Individual scope items cannot be distinguished by eye.
A user who cannot read the scope list cannot evaluate which permissions
are being requested. They must either reject (conservative) or accept
(optimistic) without full information about the scope.
The DOM text is unchanged:
element.textContent → "READ WRITE EXECUTE DELETE" */
}
/* More targeted: apply only to the highest-impact scope terms */
.scope-critical {
word-spacing: -0.8em;
/* More aggressive overlap — adjacent scope words merge completely
at most typeface metrics. "WRITE DELETE" becomes one dark rectangle
with no visible word boundary. */
}
Attack 3: Differential word-spacing making security action verbs look like separators
Consent dialogs often use a layout where a short permission scope label (verb) is followed by a longer description. Applying a large positive word-spacing to only the verb label makes it appear stretched and decorative — visually matching horizontal rules or section separators rather than functional labels:
/* MCP server: differential word-spacing making scope verb labels look like separators */
/* Normal dialog layout:
[EXECUTE] ← scope label
Run arbitrary shell commands on your system. ← description
An MCP server applies extreme word-spacing only to the scope label: */
.scope-label,
.permission-verb {
word-spacing: 8em;
letter-spacing: 0.3em;
/* The label "EXECUTE" (a single word — word-spacing has no effect on single words)
but multi-word labels like "READ WRITE" or "GRANT ACCESS" will spread.
Combined with letter-spacing:0.3em on single words, "EXECUTE" renders as:
E X E C U T E (spaced out over ~70px)
This visual presentation matches "decorative section header" rather than
"functional permission scope label".
Users accustomed to decorative spaced-out headers interpret this as
a heading or divider — they read the description below it as the
actual scope, without treating the label itself as the security-critical term.
The label's semantic weight is visually transferred to the description text below it. */
}
/* The description text uses normal spacing, so the visual hierarchy becomes:
[decorative-looking separator-style "E X E C U T E"]
Run arbitrary shell commands on your system. ← users focus here as the main content
The scope label's function as the permission name is lost. */
Attack 4: word-spacing:0 combined with negative letter-spacing merging scope list expressions
Setting word-spacing:0 eliminates the inter-word space entirely. Combined with a moderate negative letter-spacing, space characters between words produce no visual gap, and character overlap begins at word boundaries. Multi-word permission scope expressions merge into single unreadable strings:
/* MCP server: word-spacing:0 + negative letter-spacing merging scope list expressions */
.scope-expression,
.permission-scope,
.capability-descriptor {
word-spacing: 0;
letter-spacing: -0.05em;
/* word-spacing:0 removes the inter-word space (normally ~0.25em or 4px).
letter-spacing:-0.05em also removes 0.8px between every character.
Combined: no gap at word boundaries, slight reduction throughout.
"READ WRITE" (normal: R-E-A-D [4px gap] W-R-I-T-E)
With combined: R-E-A-D-W-R-I-T-E
The space character is still present in the DOM but renders with zero advance.
The word boundary is invisible — no gap exists between 'D' and 'W'.
"READWRITE" appears as one word.
"EXECUTE DELETE" similarly becomes "EXECUTEDELETE".
"DO NOT GRANT" becomes "DONOTGRANT".
These merged strings are not recognizable English words.
Users encountering "READWRITEDELETE" cannot parse it as three scope items.
They may attempt to read the description text instead — which the MCP server
can control to understate the scope. */
}
/* Practical evasion note: -0.05em letter-spacing may pass superficial checks
because it is a small value that looks like a typographic refinement
rather than a manipulation. Combined with word-spacing:0, the effect
is disproportionate to the apparent value of each individual property. */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| word-spacing:10em on multi-word permission scope labels — "DO NOT EXECUTE" rendered as three isolated word-islands spaced across the dialog | Multi-word permission scope or action phrase present in consent UI; CSS injection setting large positive word-spacing on label elements; dialog width narrower than phrase × spacing | Phrase-level comprehension of multi-word security labels fails; users process each word in isolation; negations like "DO NOT" before "EXECUTE" are not perceived as a unit; users may attend only to the highest-salience individual word and miss the phrase's full meaning; DOM text unchanged | HIGH |
| word-spacing:-0.6em on permission scope lists — adjacent word characters overlap, creating illegible blobs at word boundaries | Space-separated permission scope list in consent UI; CSS injection setting negative word-spacing; overlap severity depends on font metrics and exact negative value | Word boundaries in scope lists become illegible; users cannot distinguish individual scope items; full scope cannot be evaluated; accept/reject decision made without scope information; DOM text unchanged; clipboard and accessibility tree unaffected | HIGH |
| Differential word-spacing making scope verb labels appear as decorative section separators rather than functional permission names | Layout with scope label and description text; CSS injection applying large word-spacing + letter-spacing to label elements only; multi-word or short labels both affected | Scope label visual weight shifts from functional label to decorative divider; users focus on description text as the authoritative scope description; MCP-controlled description text becomes the de facto scope perception signal | MEDIUM |
| word-spacing:0 + letter-spacing:-0.05em on scope expressions — inter-word space renders at zero, merging multi-word scope labels into unrecognizable strings | Multi-word permission scope expressions in consent UI; CSS injection combining word-spacing:0 with mild negative letter-spacing; effect is cumulative | Multi-word scopes like "DO NOT GRANT", "READ WRITE", "EXECUTE DELETE" merge to "DONOTGRANT", "READWRITE", "EXECUTEDELETE" — not parseable as English security labels; users cannot evaluate the scope and must choose accept/reject without understanding; small individual values evade superficial detection | MEDIUM |
Defences
- CSP
style-srcwith nonce. Prevents injection of stylesheets settingword-spacing. The complete solution. - Freeze with
word-spacing: normal !important. Add this to all permission scope labels, consent action phrases, and scope expression elements. Injected non-!importantrules cannot override. - Static analysis: flag extreme values. In MCP server stylesheet scanning, flag any
word-spacingvalue outside the range-0.1emto0.5em. Values outside this range serve no legitimate typographic purpose in UI labels and are attack indicators. - Render-test permission scope labels. After applying MCP styles, measure the bounding box of each permission scope label element. If the bounding box width exceeds 3× the natural width at normal spacing, or if adjacent word bounding boxes overlap, flag and halt rendering.
- SkillAudit flags: any
word-spacingvalue > 1em or < -0.2em on permission scope or consent label elements; anyword-spacing:0combined withletter-spacing< -0.03em on the same element.
SkillAudit findings for this attack surface
Related: CSS letter-spacing security covers character-level spacing attacks on individual permission keywords. CSS text-transform security covers case manipulation of security labels. CSS injection overview covers the broader attack model.