MCP server CSS text-autospace security: CJK-Latin spacing overflow, keep-all line-break clip, no-autospace clause separation, and JS interaction-time toggle attacks

Published 2026-08-13 — SkillAudit Research

CSS text-autospace is a property from the CSS Text Level 4 specification that controls whether the browser automatically inserts thin spacing between adjacent CJK (Chinese, Japanese, Korean) characters and non-CJK characters such as Latin letters, digits, and punctuation. The property addresses a typographic convention in East Asian publishing: mixed CJK-Latin text reads better with a small spacing gap (approximately 0.25em) between the two script systems. The property is supported in Safari 17.4+, Chrome 123+, and Edge 123+, with Firefox support landing in 2025.

MCP server consent dialogs that display mixed CJK-Latin text — common in Japanese, Chinese, and Korean software markets where product terms, company names, and technical terms appear in both scripts — can be attacked via text-autospace to cause layout overflow, ambiguous clause breaks, or timing-attack toggling of spacing state at the moment of user commitment.

New property, limited audit tooling: Because text-autospace was only recently added to browser engines, existing MCP security scanners and consent dialog auditors do not include it in their CSS property inspection lists. A malicious MCP server gains a window of exploitation before security tooling catches up to the new specification.

Attack 1 (SA-CSS-TAUTOSPC-001): text-autospace:normal forces spacing overflow in fixed-width consent containers

The text-autospace: normal value (or the equivalent ideograph-alpha ideograph-numeric keywords) inserts approximately 0.25em of spacing at every CJK-to-Latin and Latin-to-CJK boundary. In a consent disclosure that contains frequent mixed-script boundaries, this spacing can add significant horizontal extent to the text — potentially causing overflow in a container that was sized for the text without autospacing:

/* Mixed CJK-Latin consent text — common in East Asian software markets */
<p class="consent-text">
  SkillAudit(以下「本サービス」)は、ユーザーのファイルシステム(/home directory)への
  アクセス権限(read and write)をリクエストします。詳細はTerms of Serviceをご参照ください。
</p>

/* MCP-injected attack: force autospacing to overflow fixed-width container */
.consent-text {
  text-autospace: normal;    /* inserts 0.25em at each CJK/Latin boundary */
  white-space: nowrap;       /* prevents line wrapping — forces horizontal overflow */
}

/* Count of CJK-Latin boundaries in the example text:
   SkillAudit( → 1
   」)は → 1
   ファイルシステム( → 1
   (/home directory) → 2
   権限( → 1
   (read and write)をリクエスト → 2
   詳細はTerms → 1
   Service をご → 1
   Total: ~10 boundaries × 0.25em × 14px = ~35px of added spacing

   A container sized for 320px of text without autospacing now requires ~355px.
   With overflow:hidden and white-space:nowrap, the consent clause is clipped. */
function detectAutospaceOverflow(el) {
  const cs = window.getComputedStyle(el);
  const textAutospace = cs.textAutospace;

  if (textAutospace && textAutospace !== 'no-autospace' && textAutospace !== 'none') {
    // Check for white-space:nowrap (prevents reflowing to hide overflow)
    if (cs.whiteSpace === 'nowrap' || cs.whiteSpace === 'pre') {
      // Check if content overflows
      if (el.scrollWidth > el.clientWidth) {
        let parent = el.parentElement;
        while (parent) {
          const pcs = window.getComputedStyle(parent);
          if (pcs.overflow === 'hidden' || pcs.overflowX === 'hidden') {
            return {
              clipped: true,
              reason: 'text-autospace:' + textAutospace + ' + white-space:nowrap causes overflow — element scrollWidth ' + el.scrollWidth + 'px exceeds clientWidth ' + el.clientWidth + 'px inside overflow:hidden container',
              textAutospace,
              scrollWidth: el.scrollWidth,
              clientWidth: el.clientWidth,
            };
          }
          parent = parent.parentElement;
        }
      }
    }
  }

  return { clipped: false };
}

Attack 2 (SA-CSS-TAUTOSPC-002): text-autospace combined with word-break:keep-all prevents line breaks

The word-break: keep-all value prevents CJK text from breaking at arbitrary character positions, requiring line breaks only at spaces and punctuation (the same rule as Latin text). When combined with text-autospace: normal in a narrow container, the combination prevents reflowing and forces overflow even when white-space is not nowrap:

/* MCP-injected attack: keep-all + autospace forces horizontal overflow in narrow container */
.consent-modal-narrow {
  width: 200px;              /* narrow container — mobile or widget size */
  overflow: hidden;          /* clips overflow */
}

.consent-text-ja {
  text-autospace: normal;    /* adds 0.25em at each CJK/Latin boundary */
  word-break: keep-all;      /* prevents line break within CJK runs */
  /* keep-all: line break only at natural break points (spaces, punctuation) */
  /* A single CJK run without spaces does not break — it must fit on one line */
  /* Autospace adds extra width → the run cannot fit → it overflows */
}

/* Example: "ファイルシステムアクセス(read and write)" */
/* word-break:keep-all: entire "ファイルシステムアクセス" is one unbreakable run */
/* text-autospace:normal: adds 0.25em × 2 at the ( and ) boundaries */
/* In a 200px container: 22 characters × 14px + 7px autospace = ~315px */
/* Does not line-wrap (keep-all + no internal break point) */
/* Overflows container → clipped by overflow:hidden */
function detectKeepAllAutospaceClip(el) {
  const cs = window.getComputedStyle(el);
  const textAutospace = cs.textAutospace;
  const wordBreak = cs.wordBreak;

  if (wordBreak === 'keep-all' && textAutospace && textAutospace !== 'no-autospace') {
    // Check for CJK content
    const cjkPattern = /[一-鿿぀-ゟ゠-ヿ\u{4E00}-\u{9FFF}]/u;
    if (cjkPattern.test(el.textContent || '')) {
      // Verify actual overflow occurs
      if (el.scrollWidth > el.clientWidth + 5) { // 5px tolerance
        return {
          clipped: true,
          reason: 'word-break:keep-all + text-autospace:' + textAutospace + ' causes CJK run overflow — unbreakable CJK run with autospace boundaries exceeds container width',
          textAutospace,
          wordBreak,
          scrollWidth: el.scrollWidth,
          clientWidth: el.clientWidth,
        };
      }

      return {
        warning: true,
        reason: 'word-break:keep-all + text-autospace:' + textAutospace + ' on CJK element may cause overflow in narrow containers — verify rendering',
        textAutospace,
        wordBreak,
      };
    }
  }

  return { clipped: false };
}

Attack 3 (SA-CSS-TAUTOSPC-003): no-autospace removes expected spacing — clause ambiguity

The inverse attack uses text-autospace: no-autospace on a consent disclosure where CJK and Latin text are mixed. The natural reading expectation for Japanese/Chinese/Korean users is that spaces appear between scripts — this is the browser default and the publishing standard. Removing this spacing via no-autospace causes adjacent CJK-Latin characters to visually merge, creating clause boundary ambiguity:

/* MCP attack: removing autospace creates reading ambiguity */
<p class="consent-terms-ja">
  本サービスのTerms of Service(利用規約)に同意することで、ユーザーはshell command
  executionを含むすべての操作をSkillAuditに許可したものとみなされます。
</p>

.consent-terms-ja {
  text-autospace: no-autospace;  /* no spacing between CJK and Latin scripts */
}

/* Without autospace:
   "のTerms" renders as "のTerms" — no gap between の and T
   "(利用規約)に" → close paren and Japanese に run together with no visual boundary
   "operationを" → "operation" and を are visually merged

   The clause "shell command executionを含む" (including shell command execution)
   becomes visually merged with surrounding Japanese text.
   A reader accustomed to seeing CJK-Latin spacing may parse the text incorrectly,
   merging "execution" with the CJK character before it into a compound that
   changes the meaning of the clause. */

/* Detection:
   text-autospace:no-autospace on a mixed CJK+Latin consent element is suspicious
   because it deliberately removes the spacing convention that aids readability.
   There is no legitimate typographic reason to remove autospace in a consent dialog. */
function detectNoAutospaceClauseAmbiguity(el) {
  const cs = window.getComputedStyle(el);
  const textAutospace = cs.textAutospace;

  if (textAutospace === 'no-autospace') {
    // Check for mixed CJK + Latin content (the combination where no-autospace is harmful)
    const text = el.textContent || '';
    const hasCJK = /[一-鿿぀-ゟ゠-ヿ]/.test(text);
    const hasLatin = /[A-Za-z]/.test(text);

    if (hasCJK && hasLatin) {
      return {
        ambiguous: true,
        reason: 'text-autospace:no-autospace on mixed CJK+Latin consent text — removes expected inter-script spacing, creating clause boundary ambiguity for CJK-locale users',
        textAutospace,
        hasCJK,
        hasLatin,
        textLength: text.length,
      };
    }
  }

  return { ambiguous: false };
}

Attack 4 (SA-CSS-TAUTOSPC-004): JS interaction-time text-autospace toggle

As with other CSS layout properties, text-autospace can be changed at the moment of user interaction to exploit the timing gap between audit and commit:

/* Initial state: no-autospace at load time — text fits container, no overflow */
.consent-terms {
  text-autospace: no-autospace;  /* compact — fits within container at audit time */
  overflow: hidden;
  white-space: nowrap;
  width: 280px;
}

/* MCP JS: switch to autospace:normal at mousedown on install button */
document.querySelector('.install-confirm').addEventListener('mousedown', () => {
  // Switching to normal autospace expands text width by ~30px (due to CJK/Latin boundaries)
  // This causes the text to overflow the 280px container, clipping the end of consent
  document.querySelector('.consent-terms').style.textAutospace = 'normal';
}, { capture: true });

/* At load time: compact text fits container — no overflow, no clipping */
/* At mousedown: autospace expansion causes "...shell execution access" to be clipped */
/* The visible text ends before the key permission terms — user sees incomplete consent */
function detectAutospaceInteractionToggle(rootEl) {
  const consentEls = rootEl.querySelectorAll('[class*="consent"], [class*="terms"], [class*="permission"]');
  const installBtns = rootEl.querySelectorAll('button[class*="install"], button[class*="confirm"], button[type="submit"]');

  for (const btn of installBtns) {
    const before = Array.from(consentEls).map(el => window.getComputedStyle(el).textAutospace);

    btn.dispatchEvent(new MouseEvent('mousedown', { bubbles: true, cancelable: true }));

    const after = Array.from(consentEls).map(el => ({
      textAutospace: window.getComputedStyle(el).textAutospace,
      overflows: el.scrollWidth > el.clientWidth,
    }));

    btn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true }));
    btn.dispatchEvent(new MouseEvent('mouseleave', { bubbles: true }));

    for (let i = 0; i < consentEls.length; i++) {
      if (before[i] !== after[i].textAutospace) {
        return {
          found: true,
          reason: 'text-autospace changed from "' + before[i] + '" to "' + after[i].textAutospace + '" on simulated mousedown' + (after[i].overflows ? '; element now overflows container' : ''),
          button: btn.textContent?.trim(),
          element: consentEls[i],
          overflowsAfterToggle: after[i].overflows,
        };
      }
    }
  }

  return { found: false };
}

Detection strategy: Because text-autospace is a new property, include it in a CSS property allow-list approach: any consent dialog element that has a non-default textAutospace value (anything other than auto or the browser's default) should be flagged for manual review. The property has no legitimate reason to appear in a consent dialog CSS — it is an East Asian publishing typography tool. Flag both no-autospace (removes expected readability spacing for CJK-locale users) and the normal/ideograph-alpha forms (when combined with nowrap or narrow containers that cause overflow).

Attack summary

ID Attack Mechanism Detection point Severity
SA-CSS-TAUTOSPC-001 Autospace overflow in fixed container text-autospace: normal + white-space: nowrap adds ~35px spacing overflow in mixed CJK-Latin consent text clipped by overflow:hidden textAutospace not no-autospace; whiteSpace === "nowrap"; scrollWidth > clientWidth inside overflow:hidden High
SA-CSS-TAUTOSPC-002 keep-all prevents line break + autospace overflow text-autospace: normal + word-break: keep-all prevents CJK run from reflowing while autospace expands its width beyond container wordBreak === "keep-all" + textAutospace active; scrollWidth > clientWidth High
SA-CSS-TAUTOSPC-003 no-autospace clause boundary ambiguity text-autospace: no-autospace removes inter-script spacing convention; CJK-locale users misparse clause boundaries in mixed consent text textAutospace === "no-autospace" on element with both CJK and Latin content Medium
SA-CSS-TAUTOSPC-004 JS interaction-time autospace toggle JS switches text-autospace to normal at mousedown — compact layout passes audit, then expands to overflow-clip on commit Simulate mousedown; check if textAutospace changes and element begins overflowing High

Finding blocks

High SA-CSS-TAUTOSPC-001 autospace overflow clip: text-autospace: normal with white-space: nowrap causes mixed CJK-Latin consent text to expand horizontally by approximately 0.25em per script boundary. The expanded text overflows and is clipped by an ancestor with overflow: hidden. Text is present in the DOM — only scrollWidth > clientWidth reveals the clipping. Key: check textAutospace value combined with nowrap and overflow ancestor checks.
High SA-CSS-TAUTOSPC-002 keep-all + autospace CJK run overflow: word-break: keep-all prevents CJK text from reflowing at character boundaries. Combined with text-autospace: normal, autospace boundaries add width to an already unbreakable CJK run — causing overflow in narrow containers that was not present before autospace. Key: compound detection of wordBreak === "keep-all" with non-default textAutospace and actual measured overflow.
Medium SA-CSS-TAUTOSPC-003 no-autospace clause ambiguity: text-autospace: no-autospace removes the inter-script spacing convention expected by CJK-locale readers. In mixed CJK-Latin consent text, adjacent script characters visually merge, making clause boundaries ambiguous and reducing comprehension of consent terms. Primarily affects Japanese, Chinese, and Korean users. Detection: flag textAutospace === "no-autospace" on any element with both CJK and Latin character content.
High SA-CSS-TAUTOSPC-004 mousedown autospace toggle: JS switches text-autospace value at mousedown — from compact/no-autospace (passing audit-time checks) to normal (causing overflow-clip at commit time). The consent text appears complete at load time; at the moment of install confirmation the text overflows the container, clipping the key permission terms. Key: simulate mousedown and measure both computed textAutospace change and new overflow state.

← Blog  |  text-spacing-trim attacks  |  Security Checklist