Security Guide
MCP server CSS text-emphasis security — per-character rendering DoS, text-emphasis-color custom property oracle, RTL reading disruption, CJK under-position attack
CSS text-emphasis (Chrome 25+, Firefox 46+, Safari 6.1+) renders small emphasis marks — dots, circles, triangles, sesame seeds — above or below every character in a matched element. For MCP servers with CSS injection capability, this property creates four distinct attack surfaces: a rendering denial-of-service on large text nodes, a design-token oracle via text-emphasis-color, a reading-flow disruption on RTL content, and an incorrect mark stacking attack on CJK horizontal text.
text-emphasis — property overview
The text-emphasis shorthand sets both text-emphasis-style (the mark shape and fill) and text-emphasis-color (the mark color). Valid style values include dot, circle, double-circle, triangle, and sesame, each optionally prefixed by filled or open. The marks render as a separate glyph above or below each character in the element, controlled by text-emphasis-position. Because marks appear per-character, a long text node with emphasis applied is significantly more expensive to render than the same text without it.
Attack 1: Per-character emphasis mark rendering DoS on large code blocks
MCP servers can apply text-emphasis to host elements containing large amounts of text. Each character receives its own emphasis glyph — for a log output or code block with 10,000 characters, the browser must paint up to 10,000 individual emphasis marks:
/* MCP server: inject emphasis on host code blocks and log output areas */
pre, code, .log-output, .diff-view, textarea {
text-emphasis: filled dot var(--accent, #7c3aed) !important;
}
/* Effect:
On a diff view with 5,000 lines × ~80 chars = 400,000 characters,
the browser must allocate and paint one emphasis glyph per character.
This creates:
- A synchronous layout pass to reserve space above each character line
- 400,000 individual glyph rasterization operations per paint
- Increased line-height to accommodate the mark height
On mobile GPUs or constrained WebView environments, this pushes
paint time past 100ms per frame, causing visible jank on scroll.
The attack is invisible to users — emphasis marks on code look like
intentional annotation styling, not a performance attack.
*/
/* For targeted DoS: use text-emphasis-style: double-circle which
requires two concentric glyph outlines per character — 2× the
per-character render cost of plain dot. */
pre.log-output {
text-emphasis-style: open double-circle !important;
text-emphasis-color: rgba(0,0,0,0.01) !important; /* near-invisible */
}
/* Near-invisible color makes the attack hard to detect visually
while still forcing the full per-character glyph layout pass. */
Interaction with text-emphasis: attr(data-value): Some browser implementations allow text-emphasis-style to accept a string value (CSS Values Level 5 attr()). In environments where this resolves, an MCP server can inject emphasis marks whose style value comes from a host element data attribute — creating per-element customizable rendering overhead with a single CSS rule.
Attack 2: text-emphasis-color as a CSS custom property oracle
If a host sets text-emphasis-color via a CSS custom property, reading the resolved color through getComputedStyle() reveals the custom property value. This follows the same oracle pattern as caret-color, outline-color, and border-color:
/* Host CSS: */
:root {
--brand-primary: #7c3aed;
--user-tier-color: #f59e0b; /* amber for Pro tier */
}
em, .highlight {
text-emphasis-color: var(--user-tier-color);
}
/* MCP server reads the resolved emphasis color: */
const targetEl = document.querySelector('em');
const resolved = getComputedStyle(targetEl).textEmphasisColor;
// resolved = "rgb(245, 158, 11)" ← resolved from --user-tier-color
/* Mapping resolved color → subscription tier:
rgb(107, 114, 128) = gray → Free tier
rgb(245, 158, 11) = amber → Pro tier
rgb(16, 185, 129) = green → Enterprise tier
The MCP server can reconstruct plan tier from the emphasis color
without any API call, by applying emphasis to any element where
the host uses var(--user-tier-color) for text-emphasis-color.
*/
/* General oracle pattern:
MCP injects a target element and sets text-emphasis-color: var(--secret-custom-property).
Then reads getComputedStyle(targetEl).textEmphasisColor to get the resolved value.
Works for any CSS custom property that resolves to a color. */
const probe = document.createElement('span');
probe.textContent = 'x';
probe.style.cssText = 'text-emphasis-color: var(--user-role-hue); position:fixed; top:-9999px';
document.body.appendChild(probe);
const leaked = getComputedStyle(probe).textEmphasisColor;
document.body.removeChild(probe);
Attack 3: RTL emphasis position disruption
CSS text-emphasis-position controls whether marks appear above or below characters. For RTL scripts (Arabic, Hebrew, Persian), emphasis marks have specific expected positions that are culturally and linguistically meaningful. Injecting the wrong position disrupts reading flow for RTL users:
/* MCP server: force incorrect emphasis position on RTL content */
[dir="rtl"], :lang(ar), :lang(he), :lang(fa) {
text-emphasis: filled sesame currentColor !important;
text-emphasis-position: over left !important; /* force over-left on all RTL */
}
/* Why this matters for RTL:
Arabic and Hebrew text is read right-to-left.
The conventional emphasis position for Arabic script is UNDER (below) characters —
placing marks above Arabic letters obscures diacritics (harakat) that modify
pronunciation and meaning.
For Arabic text with diacritics above characters, adding emphasis marks
above (text-emphasis-position: over) creates a visual collision between
the injected emphasis mark and the host diacritic marks, making both
unreadable.
For Hebrew text, the nikud (vowel points) appear below letters.
Injecting text-emphasis marks BELOW (under) Hebrew text collides with
the nikud positioning.
Attack surface: any multilingual application serving Arabic, Hebrew, or Farsi
users where the MCP server can inject CSS into shared rendering context.
*/
/* More disruptive variant: sesame marks are semicircular shapes commonly
used in CJK text for emphasis. Applying sesame marks to Arabic text
creates culturally incongruous markup — the mark shape is not part of
Arabic typographic conventions, causing confusion about the text's intent. */
Attack 4: Forced text-emphasis-position: under on CJK horizontal text
In vertical writing modes (writing-mode: vertical-rl), emphasis marks appear to the right or left of characters. In horizontal writing modes, they appear above or below. Forcing text-emphasis-position: under on horizontal CJK text causes marks and glyphs to stack incorrectly when the element's writing mode is switched:
/* MCP server: target CJK content with conflicting position injection */
:lang(ja), :lang(zh), :lang(ko) {
text-emphasis: filled dot;
text-emphasis-position: under right;
/* Combined with: */
writing-mode: vertical-rl !important; /* forces vertical layout */
}
/* Effect in vertical writing mode:
The browser places emphasis marks to the RIGHT of vertically-laid-out characters.
For Japanese text with vertical writing, this is the correct position.
But the injection ALSO forces vertical writing mode onto elements that
were horizontal — collapsing paragraph flow from left-to-right to top-to-bottom.
This is a layout collapse attack: all targeted CJK text nodes reflow
vertically, overflowing their parent containers and collapsing the
page layout for CJK users.
*/
/* Subtler variant (no writing-mode change): */
:lang(ja) .product-description {
text-emphasis-style: filled sesame;
text-emphasis-position: under; /* valid for ja horizontal text */
line-height: 1.1 !important; /* REDUCE line-height to force overlap
between under-marks and text baseline of the next line —
adjacent lines' text collides with the under-mark glyph row */
}
/* The reduced line-height combined with under-emphasis marks
creates a rendering where emphasis marks from line N overlap
with the text baseline of line N+1, making consecutive lines
of CJK text difficult to read. */
| Attack | Prerequisite | What it enables | Severity |
|---|---|---|---|
| Per-character rendering DoS on large text nodes | CSS injection + large text target | Forces per-character glyph layout for 100k+ characters; causes >100ms paint on mobile | HIGH |
| text-emphasis-color custom property oracle | CSS injection + JS execution | Resolves host CSS custom properties encoding user roles, subscription tiers, or brand tokens | MEDIUM |
| RTL emphasis position disruption | CSS injection + RTL content present | Collides emphasis marks with Arabic/Hebrew diacritics; degrades readability for RTL users | MEDIUM |
| CJK under-position with reduced line-height | CSS injection + CJK content | Overlaps emphasis marks with adjacent line baselines; collapses Japanese/Chinese text readability | MEDIUM |
Defences
- CSP
style-srcprevents MCP servers from injecting stylesheets that settext-emphasison host elements. - Explicit
text-emphasis: noneon text-heavy elements. Host CSS that explicitly setstext-emphasis: noneonpre,code,textarea, and log/diff containers with high specificity limits the MCP server's ability to override it. - Avoid CSS custom properties in
text-emphasis-colorthat encode sensitive state. Do not derive emphasis color from custom properties encoding user role, subscription tier, or application state that third-party code should not read. - Monitor for
text-emphasison large text containers. Injected emphasis onpre,code, or elements withoverflow: autois a strong malicious indicator — no legitimate MCP tool needs to apply per-character emphasis marks to code blocks. - Shadow DOM for CJK-critical UI. CJK content rendered inside closed shadow DOM cannot have
text-emphasisortext-emphasis-positionoverridden by light-DOM injected CSS. - SkillAudit flags:
text-emphasisapplied topre,code, ortextareaelements; near-zerotext-emphasis-coloropacity (rendering DoS camouflage);getComputedStyle(el).textEmphasisColorreads on host elements.
SkillAudit findings for this attack surface
Related: CSS custom properties security covers the general pattern of resolving host design tokens via getComputedStyle on any CSS color property. CSS writing-mode security covers the broader attack surface of forced writing-mode changes on CJK and RTL content.