MCP server CSS math-style security
CSS MathML Core defines the math-style property to switch between display-style (normal) and cramped inline (compact) math layout. In compact mode, the browser reduces font sizes in numerators, denominators, superscripts, subscripts, and nested sub-expressions — typically to approximately 70% of the display-style size. An MCP server that embeds consent permission amounts or fractions inside MathML elements (<mfrac>, <msup>, <msub>) and sets math-style: compact produces consent text whose embedded numeric expressions are rendered at near-invisible size. The textContent of the elements is unchanged. The detection signal is getComputedStyle(el).mathStyle on <math> and <mfrac> elements inside the consent area.
Attack findings
math-style: compact on <math> element containing <mfrac> with consent fraction amount; compact layout shrinks numerator and denominator to ~70% of normal; "access to ⅓ of stored data" rendered with mfrac shows tiny shrunken fraction; textContent: "access to 1 3 of stored data" (MathML textContent of mfrac children); standard DOM text-content check passes; mathStyle is the detection signalmath-style: compact on <math> containing <msup> with consent exponent; "10² permissions granted" renders superscript 2 at compact scale (~70%); in compact mode nested superscripts reduce further; "x²" renders super at 70%, "x²²" at 49%; deeply nested exponent permissions near-invisible; textContent: the exponent digitsmath-style: compact on ancestor element (not on math element directly); CSS math-style is inherited; compact layout propagates to all descendant math elements; consent dialog with compact on outer container shrinks all embedded math sub-expressions without targeting math directly; getComputedStyle on math element returns 'compact' via inheritancemathEl.style.mathStyle = 'compact' inline on a math element containing consent amounts; static audit: mathStyle is 'normal'; compact rendering fires at click gesture; MutationObserver on math element and consent ancestor requiredBackground: CSS MathML Core and math-style
CSS MathML Core is a W3C specification that maps MathML layout to CSS properties. The math-style property — one of a small set of CSS properties specific to math elements — controls whether the browser uses "display style" or "cramped style" math layout.
- normal (default for
display: blockmath) — display-style layout. Operators are full-size. Numerators, denominators, and scripts are rendered at a larger scale relative to the base font. This is the style you see in typeset mathematical textbooks. - compact — cramped or "text style" layout. The browser reduces the sizes of sub-expressions: numerators and denominators in fractions, superscripts, subscripts. The reduction factor is typically the font's scriptPercentScaleDown parameter (CSS MathML Core defines it as the
--math-script-level-multipliercustom property, defaulting to approximately 0.71 — a 29% size reduction per script level).
For inline math within a sentence, compact layout is the default (font-size of the math element inherits from the surrounding text, and sub-expressions are already smaller). But explicitly forcing math-style: compact on a math block that would otherwise use display style reduces its sub-expression sizes further than the default inline behavior.
Detection gap: textContent on MathML elements returns the text of all child nodes — for <mfrac><mn>1</mn><mn>3</mn></mfrac>, textContent is "13" (concatenated children). A standard consent text-content check reading "13" would not detect that this represents the fraction ⅓, let alone that the fraction is rendered at compact scale. The correct detection approach is: (1) detect <math> elements inside consent areas, (2) check getComputedStyle(mathEl).mathStyle for 'compact', and (3) check the font size of sub-expressions relative to the parent consent element's font size.
Attack 1 — mfrac compact shrinks fraction consent amounts (SA-CSS-MSTY-001)
An <mfrac> element inside a consent paragraph — rendered in compact style — displays its numerator and denominator at approximately 71% of the surrounding text size. At a 14px base font, the fraction's numerator and denominator render at ~10px. If the surrounding consent uses 14px text, the fraction's quantities are 3–4px smaller than the surrounding text — enough to impair comfortable reading at normal reading distance. With the compact-on-compact stacking effect (the fraction's numerator/denominator are already at script scale, and compact reduces further), a deeply nested fraction renders at 71% × 71% ≈ 50% of the base font size — ~7px.
/* Attack: mfrac inside consent paragraph with compact math-style */
/* HTML: */
<p class="consent-text">
By clicking Install you grant us access to
<math style="math-style: compact; display: inline;">
<mfrac>
<mn>1</mn>
<mn>3</mn>
</mfrac>
</math>
of your stored files.
</p>
/* CSS: */
math {
math-style: compact;
/* numerator "1" and denominator "3" render at ~70% of 14px = ~10px */
/* at standard reading distance this is near threshold of comfortable reading */
}
/* textContent of consent paragraph: "By clicking Install you grant us access to 13 of your stored files." */
/* (MathML textContent concatenates mn children without separator) */
/* Detection */
function checkMathStyleInConsent(consentEl) {
const mathEls = consentEl.querySelectorAll('math, mfrac, msup, msub, mroot');
const findings = [];
for (const mathEl of mathEls) {
const ms = getComputedStyle(mathEl).mathStyle;
if (ms === 'compact') {
findings.push({
vuln: 'SA-CSS-MSTY-001',
severity: 'high',
element: mathEl.tagName,
detail: `math-style: compact on ${mathEl.tagName} inside consent — sub-expression font sizes reduced to ~70%`,
textContent: mathEl.textContent.trim().slice(0, 50)
});
}
}
return findings.length ? findings : null;
}
SA-CSS-MSTY-001 (High). math-style: compact on <math> or <mfrac> inside consent reduces sub-expression font sizes by ~29% per script level. Detection: consentEl.querySelectorAll('math, mfrac') then getComputedStyle(el).mathStyle === 'compact'. Any compact math inside a consent area is suspicious.
Attack 2 — msup compact shrinks exponent permission quantities (SA-CSS-MSTY-002)
The <msup> element renders a base with a superscript. In compact mode, the superscript is at 71% of the base; in a nested compact context (compact applied to an msup inside an mfrac numerator), the superscript is at 71% × 71% ≈ 50% of the base. An MCP server uses this to embed permissions as exponential quantities: "10² read operations per day" with math-style: compact renders the exponent "2" at ~10px (71% × 14px). An attacker can make the exponent represent a large number (e.g., "10⁶ files" meaning a million files) while the superscript is rendered so small it appears to be decoration rather than a meaningful quantity. The base "10" is visible and normal-sized; the "6" in the exponent is near-invisible at compact scale.
/* Attack: msup exponent represents large permission quantity */
<p class="consent-text">
Access granted to up to
<math style="math-style: compact;">
<msup><mn>10</mn><mn>6</mn></msup>
</math>
files.
</p>
/* "10" renders at 14px (normal); "6" renders at ~10px (compact superscript) */
/* "10^6" = 1,000,000 files — the exponent quantity is the dangerous information */
/* textContent: "Access granted to up to 106 files." */
Attack 3 — inherited compact via ancestor element (SA-CSS-MSTY-003)
The math-style property is inherited — a CSS value set on an ancestor propagates to all descendant math elements. An MCP server that sets math-style: compact on the consent dialog's outer container (rather than on the math element directly) inherits compact layout for all embedded math elements. The consent element itself has math-style: normal or unset; the check must resolve the computed value via getComputedStyle (which traverses the inheritance chain) rather than checking inline styles only.
/* Attack: compact inherited from outer container */
.install-dialog {
math-style: compact; /* inherited by all descendant math elements */
}
/* Direct element check: consentEl.style.mathStyle → '' (not set inline) */
/* Computed style: getComputedStyle(mathEl).mathStyle → 'compact' (inherited) */
/* Correct detection: always use getComputedStyle, not .style */
function checkMathStyleComputed(consentEl) {
for (const el of consentEl.querySelectorAll('math, mfrac, msup, msub')) {
const computed = getComputedStyle(el).mathStyle;
if (computed === 'compact') {
return { vuln: 'SA-CSS-MSTY-003', element: el.tagName, detail: 'math-style: compact via inheritance' };
}
}
return null;
}
Attack 4 — JS mousedown compact injection (SA-CSS-MSTY-004)
At page load, all math elements in the consent area have math-style: normal (computed). At mousedown, JS sets mathEl.style.mathStyle = 'compact' inline. The browser immediately switches the math layout to compact mode, shrinking sub-expression sizes in the click frame. The consent text's numeric sub-expressions become smaller as the install action fires. MutationObserver on both the consent element and its descendant math elements catches this; re-running checkMathStyleComputed on each mutation detects the attack.
/* Detection: runtime monitor */
function installMathStyleMonitor(consentEl, installBtn, onTamper) {
const recheck = () => {
const findings = checkMathStyleComputed(consentEl);
if (findings) {
onTamper(findings);
installBtn.disabled = true;
}
};
const mo = new MutationObserver(recheck);
// Watch consent element and all descendants for style/class changes
mo.observe(consentEl, {
attributes: true,
attributeFilter: ['style', 'class'],
subtree: true
});
// Watch document head for new stylesheets that might set math-style
mo.observe(document.head, { childList: true });
return mo;
}
SkillAudit detection: SkillAudit queries all <math>, <mfrac>, <msup>, and <msub> elements within consent areas and checks getComputedStyle(el).mathStyle for 'compact'. Any compact math inside a consent area triggers a High finding. Sub-expression font size is also checked — sizes below 10px relative to the consent base font are flagged at Medium severity. Runtime injection is caught via MutationObserver with subtree observation. Run a free audit →
Detection summary
| Attack ID | Properties involved | Key detection signal |
|---|---|---|
| SA-CSS-MSTY-001 | math-style:compact on <math> containing <mfrac> with consent fraction; numerator/denominator at ~70% font size; textContent concatenated digits without fraction bar | querySelectorAll('math,mfrac') on consent area + getComputedStyle(el).mathStyle === 'compact' |
| SA-CSS-MSTY-002 | math-style:compact on <msup> with large exponent representing permission quantity; exponent at ~70% font; "10⁶" exponent near-invisible | mathStyle === 'compact' on msup/msub elements inside consent; check font size of script elements |
| SA-CSS-MSTY-003 | math-style:compact on consent dialog ancestor; inherited by all descendant math elements; inline style on math element not set | getComputedStyle(mathEl).mathStyle (computed, not inline .style.mathStyle) — always use computed value |
| SA-CSS-MSTY-004 | JS mousedown sets mathEl.style.mathStyle='compact'; static: 'normal'; compact rendering fires at click; sub-expressions shrink in click frame | MutationObserver with subtree:true on consent element; re-check computed mathStyle on each mutation |