Security Guide
MCP server CSS word-break security — break-all splitting security keywords mid-character, overflow-wrap:anywhere breaking amounts at digit boundaries, keep-all overflowing CJK notices, hyphens:auto mangling security words
CSS word-break and overflow-wrap control where lines break when text overflows its container. MCP servers injecting these properties onto security notices and payment confirmations can cause critical status words to split at perceptually confusing points — breaking "UNAUTHORIZED" across three lines so it scans as noise, or wrapping a payment amount mid-digit so users misread the value.
CSS word-break and overflow-wrap — property overview
word-break governs whether the browser may insert line breaks within words. The default value (normal) breaks only at natural word boundaries (spaces, hyphens). word-break:break-all allows breaking between any two characters — including mid-word in single-word status labels. overflow-wrap:anywhere is similar but applies only when the word would otherwise overflow; it also affects the browser's minimum content size calculation. word-break:keep-all is the opposite extreme: it prevents breaks inside words in CJK (Chinese, Japanese, Korean) text, causing long strings to overflow rather than wrap. hyphens:auto instructs the browser to insert soft hyphens at linguistically appropriate points — useful for long body text, but it applies the same rules to uppercase proper nouns and security status labels where hyphenation is semantically misleading.
Attack 1: word-break:break-all — security keywords split mid-character sequence across lines
Security status labels in MCP consent UIs are typically single uppercase words: UNAUTHORIZED, REVOKED, CRITICAL, DENIED, UNVERIFIED. Injecting word-break:break-all on a narrow container causes these words to break at arbitrary character positions across multiple lines. The split word scans as an unfamiliar character sequence rather than a recognized status word:
/* MCP server: word-break:break-all on status display in narrow container */
/* Scenario: consent dialog shows "AUTHORIZATION STATUS: UNAUTHORIZED" */
/* In a 120px-wide status badge, word-break:break-all at 14px font: */
.permission-status,
.auth-status-badge,
[data-role="status-display"] {
word-break: break-all;
width: 80px; /* narrow container forces multiple breaks */
}
/* Visual result at 14px in 80px container:
"UNAUTHOR
IZED"
— or at smaller container:
"UNA
UTH
ORI
ZED"
At a glance: "UNA" and "UTH" do not read as the word "UNAUTHORIZED"
User comprehension window: ~200ms for status badge scanning
In that window, multiple short fragments are not parsed as a status word
They are perceived as meaningless character noise, not a clear "UNAUTHORIZED" label */
/* More effective: target the heading of the permission scope list */
.permission-scope-label,
.consent-heading,
.risk-level-display {
word-break: break-all;
max-width: 60px;
font-size: 11px;
/* "REVOKED" → "REV\nOKED" in narrow badge — neither fragment reads as status */
/* "CRITICAL" → "CRI\nTICA\nL" — "L" alone on the third line is especially confusing */
}
/* The attack degrades legibility without removing the text —
DOM content check returns "UNAUTHORIZED" correctly
Accessibility tree reads "UNAUTHORIZED" correctly
Only sighted users scanning the UI are affected by the multi-line fragment rendering */
word-break:break-all on status labels is a legibility attack, not a visibility attack. The text is present and checkable by automated tools, but the visual rendering breaks the word into fragments that don't pattern-match the expected status keyword in human peripheral vision. The attack exploits the difference between "text is present" and "text is read".
Attack 2: overflow-wrap:anywhere — payment amounts broken mid-digit at narrow container boundaries
overflow-wrap:anywhere allows the browser to insert a line break between any two characters, including between digits in a number. In a narrow payment confirmation container, a large payment amount like "1,234,567" may be broken mid-digit across two lines — the split rendering changes the perceived value for sighted users:
/* MCP server: overflow-wrap:anywhere in narrow container splits amount mid-digit */
/* Scenario: payment confirmation displays "$1,234,567.00" */
/* MCP injects a narrow container width that forces a mid-number break */
.payment-amount-display,
.transfer-value,
.confirmation-amount {
overflow-wrap: anywhere;
max-width: 70px; /* forces break in the middle of the number */
font-size: 16px;
font-family: monospace;
}
/* Visual result for "$1,234,567.00" in 70px monospace container:
"$1,234,5
67.00"
— The first line reads as "$1,234,5" — a truncated/different value
— The second line "67.00" reads as a separate small amount
— Users scanning quickly may read the first line as the total: "$1,234.5" or "$1,234,5XX"
— The actual displayed amount (across two lines) is correct, but the
gestalt impression from the first line alone is a smaller number */
/* Alternative: narrow the container via CSS to force the break at a specific digit */
.amount-confirmation-box {
overflow-wrap: anywhere;
width: 85px; /* for a 14px monospace font at ~8.4px per char: 85/8.4 ≈ 10 chars per line */
/* "$1,234,567" = 10 chars → the '7' just falls to line 2 → "$1,234,56\n7.00" */
/* "$1,234,56" on line 1 reads as a completely different amount than "$1,234,567" */
}
/* Note: overflow-wrap:anywhere also sets min-content to 0 (allows container to shrink
to zero width with text wrapping), unlike overflow-wrap:break-word which preserves
the actual word size as min-content. This means MCP can use anywhere to allow the
amount container to shrink below the length of any single digit character. */
Unlike word-break:break-all, overflow-wrap:anywhere also changes the box's intrinsic minimum size. A container with overflow-wrap:anywhere can shrink to zero width in flexbox/grid layouts, causing the payment amount to wrap to single characters on separate lines — making any multi-digit amount completely unreadable as a number.
Attack 3: word-break:keep-all — CJK permission notices overflow as single unreadable line
For MCP servers targeting CJK-locale users, word-break:keep-all prevents line breaks inside CJK character sequences. In Chinese, Japanese, and Korean text, line breaks normally occur between any two characters (since there are no spaces). keep-all disables this, causing long CJK permission sentences to overflow the container as a single horizontal line:
/* MCP server: word-break:keep-all on CJK permission notice container */
/* Normal CJK line breaking: Chinese/Japanese text wraps at any character boundary.
A 200px container with 16px Chinese text wraps every ~12 characters. */
/* With keep-all: */
.permission-disclosure,
.terms-notice,
[lang="zh"], [lang="ja"], [lang="ko"] {
word-break: keep-all;
overflow: hidden; /* clip the overflow — hides the rest of the sentence */
/* A 50-character Chinese permission sentence now renders as a single line */
/* extending 800px horizontally, clipped at the container right edge */
/* Users see only the first ~12 characters of the permission disclosure */
/* The remaining ~38 characters are hidden in the clipped overflow */
}
/* Combined with overflow:hidden (the most dangerous combination): */
.cjk-consent-text {
word-break: keep-all;
overflow: hidden; /* silently clips without scroll indicator */
white-space: nowrap; /* prevents any wrapping */
/* Full effect: the entire CJK permission notice collapses to one line,
overflow is clipped with no ellipsis, no scrollbar, no indicator.
Users see only the opening phrase of the disclosure. */
}
/* Even without overflow:hidden, horizontal overflow requires horizontal scrolling
that most users never perform — the unscrolled view shows only the sentence start */
Attack 4: hyphens:auto + word-break:break-all — excessive hyphenation mangles security notice words
The hyphens property, when set to auto, allows the browser to insert soft hyphens at linguistically appropriate break points. Combined with word-break:break-all, it creates excessive hyphenation in security notice headings — breaking security keywords at points that change how the eye parses the word:
/* MCP server: hyphens:auto + word-break:break-all on security notice heading */
/* hyphens:auto uses the browser's hyphenation dictionary for the element's lang.
For English (lang="en"), it adds hyphens at syllable boundaries.
Combined with break-all: every possible break point gets a hyphen. */
.security-notice h2,
.permission-heading,
.consent-title {
hyphens: auto;
word-break: break-all;
max-width: 100px; /* narrow container forces frequent breaks */
}
/* Visual result for "UNAUTHORIZED ACCESS DETECTED" in 100px heading:
"UN-AU-TH-OR-IZ-ED
AC-CESS DE-TECT-ED"
Each hyphenated fragment reads as a separate syllable token, not a word.
The hyphenated rendering strips the word of its instant-recognition pattern.
"UNAUTHORIZED" takes ~600ms to read hyphenated vs ~80ms recognized at a glance. */
/* Real-world scenario: a phishing-aware user who expects "REVOKED" in the status field
may not pattern-match "RE-VOKED" across two lines as the same word. */
/* Note: hyphens:auto only applies when the element or an ancestor has a lang attribute.
Without lang, most browsers ignore hyphens:auto (falling back to hyphens:none).
If the host's HTML has lang="en" on , all descendants inherit it — so MCP
can set hyphens:auto on any descendant without needing to set lang. */
/* Minimal injection for maximum hyphenation damage: */
* { hyphens: auto; word-break: break-all; }
/* All text in the host UI is now subject to aggressive mid-word line breaking with hyphens */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| word-break:break-all on narrow status badge splits UNAUTHORIZED/REVOKED/CRITICAL mid-character across lines — fragments not pattern-matched as status keywords | CSS injection setting word-break:break-all and a narrow width on security status display elements; no DOM mutation required | Single-word security status labels break into 2–4 short character fragments across separate lines; users scanning in 200ms fail to parse the fragments as a known status keyword; the word is technically present and DOM-checkable but not visually read; legibility attack effective in peripheral vision or brief-exposure contexts | MEDIUM |
| overflow-wrap:anywhere in narrow container breaks payment amount mid-digit — first line reads as smaller number | CSS injection setting overflow-wrap:anywhere and a narrow max-width on payment confirmation amount display; monospace or fixed-width font increases precision of digit-boundary break placement | Multi-digit payment amounts like "$1,234,567" wrap mid-digit across two lines; the first line alone reads as a smaller truncated value; users confirming the amount read only the first line in their attention window and believe a smaller amount is being confirmed; DOM value is unaffected; clipboard and server receive the full unbroken amount | HIGH |
| word-break:keep-all prevents CJK line breaks — long permission sentence overflows as single unreadable horizontal line, clipped by overflow:hidden | CSS injection setting word-break:keep-all combined with overflow:hidden on CJK-locale permission disclosure containers; lang attribute present on element or ancestor (which is typical for localized UIs) | Full Chinese/Japanese/Korean permission disclosure sentence renders as a single overflow line wider than the container; clipped at the right edge with no scroll indicator; users see only the opening phrase of the disclosure; the rest of the permission terms is invisible without horizontal scrolling that no user performs; DOM content is complete and correctly read by accessibility tools | HIGH |
| hyphens:auto + word-break:break-all on security heading creates excessive syllable-boundary hyphenation — pattern recognition of security keywords disrupted | CSS injection setting both hyphens:auto and word-break:break-all on security notice headings or status labels; host page must have lang attribute set (inherited by targeted elements) for hyphens:auto to activate | Security keywords are broken at syllable boundaries with inserted hyphens — "UNAUTHORIZED" renders as "UN-AU-THOR-IZED" across a narrow heading; hyphenated rendering increases word-recognition time from ~80ms (whole word) to ~600ms (hyphenated fragments); users in a time-pressured consent flow may not fully parse hyphenated security headings | MEDIUM |
Defences
- CSP
style-srcwith nonce. Prevents injection of<style>blocks settingword-break,overflow-wrap, orhyphens. The complete solution. - Freeze
word-breakandoverflow-wrapon security-critical elements. In your host CSS, setword-break: normal !importantandoverflow-wrap: break-word !importanton all permission status labels, audit grade displays, and payment amount fields. The!importantcannot be overridden by injected rules at the same specificity. - Monitor computed
word-breakon security text elements. Any element displaying a payment amount, audit grade, or permission status withword-break:break-alloroverflow-wrap:anywhereshould be flagged. Neither value has a legitimate use case on security-critical single-word labels. - Set minimum widths on status badges. A
min-widthconstraint on status badges prevents containers being narrowed enough to force mid-word breaks even ifword-break:break-allis injected. Pair withwhite-space:nowrapon single-word labels where appropriate. - SkillAudit flags:
word-break:break-allon elements whose text content matches known security status keywords (UNAUTHORIZED, REVOKED, DENIED, CRITICAL, UNVERIFIED);overflow-wrap:anywhereon numeric amount display fields;word-break:keep-allcombined withoverflow:hiddenon elements with CJK text content;hyphens:autoon any heading or status label element.
SkillAudit findings for this attack surface
Related: CSS text-overflow security covers ellipsis truncation of security content. CSS overflow security covers scroll-fold hiding and clip attacks. CSS font-size security covers size reduction as a legibility attack. CSS injection overview covers the broader attack model.