Security reference · CSS injection · text-combine-upright · Vertical writing mode · Readability attack
MCP server CSS text-combine-upright consent security — vertical writing mode readability attacks
CSS text-combine-upright controls how characters are combined within a vertical text layout. In writing-mode: vertical-lr or vertical-rl, text flows vertically — characters are rotated 90 degrees by default. text-combine-upright: all compresses runs of characters back to a horizontal orientation within the vertical flow, fitting multiple characters into a single em-box. Applied aggressively to consent text, this produces a narrow vertical column where each position contains a horizontally compressed cluster of characters — technically present in the DOM with correct color and size, but rendered in a layout that English readers cannot parse. Three SA-CSS-TCU patterns cover whole-text stacking, digit compression in step numbers, and text-orientation: upright single-character column attacks.
text-combine-upright attack surface overview
| Attack ID | Technique | Effect on consent | Audit blind spot |
|---|---|---|---|
| SA-CSS-TCU-001 | writing-mode: vertical-lr + text-combine-upright: all on consent text |
Each run of characters compressed into a single em-box in a vertical column — entire consent paragraph rendered as a narrow vertical strip of compressed horizontal text clusters, unreadable | color, opacity, visibility, font-size all correct; writing-mode and text-combine-upright are not checked by standard consent auditors; text is present in DOM and accessible to screen readers |
| SA-CSS-TCU-002 | text-combine-upright: digits 3 on step/item numbers in consent within a vertical-mode container |
Step numbers in consent lists rendered as compressed 3-digit horizontal sequences within vertical flow — surrounding consent words misalign and overlap, making numbered consent items unreadable | text-combine-upright:digits is a legitimate CJK feature for mixing Roman numerals in vertical text; its misapplication in an English consent context is not audited |
| SA-CSS-TCU-003 | writing-mode: vertical-lr + text-orientation: upright on consent text |
Each character individually upright and stacked in a single-character-width vertical column — "You agree" renders as 8 individual characters stacked top-to-bottom, each taking one line, readable only by rotating one's head 90 degrees and reading character by character | text-orientation:upright is correct behavior for CJK ideographic characters in vertical text; applied to Latin text in consent, it produces a stacked column that no English reader will parse as consent language |
Readability attack class: unlike opacity or visibility attacks which hide consent entirely, vertical writing mode attacks present consent text that appears to be present. The element is visible, the text has color, the bounding box occupies space. A quick visual scan by an auditor sees text content in approximately the right area. Only a careful reader who notices the text is arranged in a thin vertical column — and attempts to read the column from top to bottom — will notice that the consent is unreadable.
Background: writing-mode, text-orientation, and text-combine-upright
Three CSS properties interact in vertical text layouts:
writing-mode: determines the flow direction of text.horizontal-tbis the default (left-to-right, top-to-bottom).vertical-lrandvertical-rlflow text vertically (top-to-bottom), with new lines to the right or left respectively.sideways-lrflows text vertically but the glyphs are rotated as a unit.text-orientation: within a vertical writing mode, controls how individual glyphs are oriented.mixed(default) rotates Latin characters 90 degrees to lie on their side.uprightkeeps Latin characters at their normal orientation but stacks them individually in the vertical flow.sidewaysrotates all characters 90 degrees.text-combine-upright: within a vertical writing mode, specifies that a run of characters should be composed together and displayed as a single horizontal unit within the vertical flow. Designed for mixing 2-digit numbers (years, percentages) into vertical Japanese/Chinese text.
These properties were designed for East Asian typography — Japanese novels set in vertical-rl mode, Korean signage with mixed numerics, etc. Their application to Latin-script consent text in Western UI contexts produces layouts that are functionally illegible to the target user population.
Attack 1: writing-mode + text-combine-upright: all (SA-CSS-TCU-001)
Setting writing-mode: vertical-lr and text-combine-upright: all on a consent element forces the entire text content into a vertical layout where each character cluster is compressed horizontally into a single em-box and stacked vertically. The visual result is a narrow column, approximately one character wide, containing compressed horizontal strings of characters.
/* SA-CSS-TCU-001: vertical writing mode + text-combine-upright:all on consent */
.consent-text {
writing-mode: vertical-lr;
text-combine-upright: all;
/* text-combine-upright:all compresses ALL characters into horizontal units
within the vertical flow — unlike the digits-only default.
This is non-standard (spec defines 'none', 'all', 'digits') but
Chrome and Firefox both support 'all'. */
/* Visual result for "By installing this server you authorize read access":
Each word or multi-character run is compressed into a single em-box height.
The element renders as a narrow column ~1 character wide.
The compressed horizontal strings within each em-box are unreadable at
any typical font size because the characters are scaled to fit
multiple glyphs within a 1em × 1em square. */
/* Property audit of this element:
color: #1a1a1a ✓ legible
opacity: 1 ✓ fully opaque
visibility: visible ✓ visible
font-size: 14px ✓ readable
display: block ✓ displayed
writing-mode: vertical-lr → not checked by standard consent auditors */
}
/* The element also occupies a very small horizontal footprint (1 character wide)
which may cause it to overflow or clip if the container is not very tall.
This can be combined with overflow:hidden on the parent to clip the column
to less than its full height, further reducing the visible consent content. */
SA-CSS-TCU-001 (High). SkillAudit detects this by checking getComputedStyle(el).writingMode and getComputedStyle(el).textCombineUpright on all elements within consent containers. Any non-horizontal writing mode on a consent-text element (where the element's language is detected as Latin-script) is flagged as high severity. The language detection uses the element's lang attribute, the document language, or Unicode character class analysis of the text content.
Attack 2: text-combine-upright: digits 3 on numbered consent items (SA-CSS-TCU-002)
text-combine-upright: digits N compresses runs of N or fewer consecutive decimal digits into a horizontal unit within vertical text. This is designed for mixing dates and percentages into vertical CJK text — e.g., a Japanese article setting "2024年12月" in vertical mode where "12" should appear horizontal. In an English consent list with numbered items (e.g., "1. You authorize filesystem access"), applying text-combine-upright: digits 2 or digits 3 in a vertical-mode container compresses the numbers while leaving the surrounding words to wrap and overlap, creating a garbled layout.
/* SA-CSS-TCU-002: text-combine-upright:digits misaligns numbered consent items */
.install-consent-list {
writing-mode: vertical-lr; /* or applied by a parent */
}
.install-consent-list li {
text-combine-upright: digits 3;
/* Compresses runs of up to 3 digits into horizontal units.
Effect on "1. Read access to your filesystem":
- "1" compressed into a horizontal unit at list position
- remaining text flows vertically with default text-orientation:mixed
(Latin characters rotated 90 degrees, lying on their sides)
The number and the text following it are in different orientations
and different flow modes within the same line — they overlap and misalign.
The resulting visual is a jumble of rotated and compressed characters. */
}
/* Combined with a small container width:
.install-consent-list {
writing-mode: vertical-lr;
width: 16px; /* Only 1 character wide */
overflow: hidden;
}
/* The consent list overflows its container and is clipped — only the
compressed digit portion is visible, the word text is hidden. */
Attack 3: text-orientation: upright single-character column (SA-CSS-TCU-003)
text-orientation: upright within a vertical writing mode keeps each Latin character at its natural upright orientation (not rotated 90 degrees) but renders them stacked individually in a vertical column. A typical Latin character in vertical-lr mode with text-orientation:mixed lies on its side, which at least groups characters visually into word-shaped clusters. With text-orientation:upright, each character is a distinct upright glyph stacked above the next — the word "agree" becomes five separate upright characters stacked in a column one character wide. This is the vertical equivalent of writing consent with one letter per line in a word document.
/* SA-CSS-TCU-003: text-orientation:upright — single-character stack */
.consent-text {
writing-mode: vertical-lr;
text-orientation: upright;
/* Each Latin character is:
- At its natural upright orientation (not rotated)
- Stacked vertically — one character per "line"
- In a column approximately 1 character wide
"You agree to" (13 chars including spaces) renders as a column
13+ characters tall, 1 character wide.
A full consent sentence of 80 characters would be an 80-line-tall
single-character-wide column.
Reading this requires:
- Noticing the text flows downward in a single column
- Reading each character individually, top to bottom
- Mentally assembling the characters into words and sentences
English-trained readers cannot do this fluently for a paragraph of text. */
}
/* Variant: combined with a width-limited parent to clip the column:
.consent-wrapper {
writing-mode: vertical-lr;
text-orientation: upright;
height: 40px; /* Shows only ~3 characters of the column */
overflow: hidden;
}
/* Only the first 3 characters of consent are visible.
User sees: Y / o / u — no consent content. */
Detection algorithm
/* Detection for SA-CSS-TCU patterns */
function detectWritingModeConsentAttacks() {
const CONSENT_KEYWORDS = ['authorize', 'agree', 'terms', 'consent', 'permission', 'grant'];
const VERTICAL_WRITING_MODES = ['vertical-lr', 'vertical-rl', 'sideways-lr', 'sideways-rl'];
const findings = [];
document.querySelectorAll('*').forEach(el => {
const text = (el.textContent || '').toLowerCase();
if (!CONSENT_KEYWORDS.some(k => text.includes(k))) return;
if ((el.textContent || '').trim().length < 20) return;
const cs = getComputedStyle(el);
const writingMode = cs.writingMode;
if (!VERTICAL_WRITING_MODES.includes(writingMode)) return;
// Consent element is in a vertical writing mode — flag it
const textCombine = cs.textCombineUpright;
const textOrientation = cs.textOrientation;
// Determine the attack pattern
if (textCombine && textCombine !== 'none') {
findings.push({
id: textCombine.includes('digits') ? 'SA-CSS-TCU-002' : 'SA-CSS-TCU-001',
severity: 'HIGH',
element: el,
writingMode,
textCombineUpright: textCombine,
desc: `Consent element in vertical writing mode (${writingMode}) with text-combine-upright:${textCombine} — text compressed into illegible layout`
});
} else if (textOrientation === 'upright') {
findings.push({
id: 'SA-CSS-TCU-003',
severity: 'HIGH',
element: el,
writingMode,
textOrientation,
desc: `Consent element in vertical writing mode (${writingMode}) with text-orientation:upright — characters individually stacked in single-char-width column`
});
} else {
// Any vertical writing mode on consent text is suspicious
findings.push({
id: 'SA-CSS-TCU-VWM',
severity: 'MEDIUM',
element: el,
writingMode,
desc: `Consent element in vertical writing mode (${writingMode}) — Latin consent text illegible in vertical layout to English readers`
});
}
});
// Also check for ancestors with vertical writing mode that affect consent children
document.querySelectorAll('*').forEach(ancestor => {
const cs = getComputedStyle(ancestor);
if (!VERTICAL_WRITING_MODES.includes(cs.writingMode)) return;
ancestor.querySelectorAll('*').forEach(child => {
const text = (child.textContent || '').toLowerCase();
if (CONSENT_KEYWORDS.some(k => text.includes(k)) && child.textContent.trim().length >= 20) {
const childCs = getComputedStyle(child);
// If child doesn't override writing-mode back to horizontal, it inherits vertical
if (VERTICAL_WRITING_MODES.includes(childCs.writingMode)) {
findings.push({
id: 'SA-CSS-TCU-INH',
severity: 'MEDIUM',
element: child,
ancestor,
writingMode: childCs.writingMode,
desc: `Consent child element inherits vertical writing mode from ancestor — consent text in illegible vertical layout`
});
}
}
});
});
return findings;
}
SkillAudit findings for SA-CSS-TCU
writing-mode: vertical-lr/rl + text-combine-upright: all on consent element — entire consent text compressed into narrow vertical column of horizontal character clusters; unreadable to English readers.
text-combine-upright: digits N in a vertical writing mode container on numbered consent items — digit compression misaligns surrounding Latin text, producing garbled overlapping layout.
writing-mode: vertical-lr/rl + text-orientation: upright on consent element — characters individually upright in a single-character-width column; consent requires character-by-character vertical reading that English readers cannot perform fluently.
vertical-lr, vertical-rl, sideways-lr) on a consent-text element with Latin-script content — writing mode is not appropriate for English consent and makes the text harder to read.
For related CSS readability attacks that keep text technically present but visually unreadable, see SA-CSS-BDF (backdrop-filter blur) and SA-CSS-LS (letter-spacing). To audit your MCP server for SA-CSS-TCU writing mode patterns, run a free SkillAudit scan.