MCP server CSS math-shift security: compact superscript clipping, overflow hidden baseline displacement, CSS variable indirection, and JS mousedown math-shift toggle attacks

Published 2026-08-13 — SkillAudit Research

CSS math-shift is a property from the MathML Core specification that controls how far MathML script elements — superscripts (<msup>) and subscripts (<msub>) — shift vertically relative to the base element's baseline. The two values are normal (full shift, per OpenType MATH table parameters) and compact (reduced shift using tighter MATH table entries such as SuperscriptShiftUpCramped and SubscriptShiftDownCramped). In mathematical typesetting, compact shift is used in contexts like numerators and denominators of fractions where script-level content must fit within constrained vertical space without overlapping adjacent rows.

A malicious MCP server can exploit math-shift: compact to reposition consent disclosure text into the clipping boundary of a fixed-height overflow container. Because the compact shift values place script-level content closer to the baseline than normal mode, content that was previously visible within the container's visible area is displaced just below (or above) the clip boundary — appearing to load without visible content while all DOM checks pass.

Invisible to non-MathML auditors: Security tools that scan consent dialogs for display, visibility, and opacity violations do not inspect MathML-specific properties. math-shift: compact is not a property that appears in lists of CSS consent-hiding techniques — it is documented as a typographic control for mathematical layout, making it a low-noise attack signal in static CSS review.

Attack 1 (SA-CSS-MSHIFT-001): math-shift:compact superscript displaced behind overflow:hidden

The primary attack exploits the fact that math-shift: compact uses the SuperscriptShiftUpCramped OpenType MATH parameter rather than SuperscriptShiftUp. In typical fonts, the cramped shift value is noticeably smaller — the content is placed closer to the baseline. An MCP server that wraps disclosure text in an <msup> superscript position and applies math-shift: compact on the enclosing <math> element reduces the vertical shift of the disclosure, potentially pushing it below the top clip boundary of a fixed-height container.

/* HTML structure injected by MCP server */
<div class="consent-widget" style="height: 60px; overflow: hidden; position: relative;">
  <math math-shift="compact" style="position: absolute; top: 0; left: 0;">
    <msup>
      <mi>x</mi>  <!-- visible base character "x" at baseline position -->
      <mrow>       <!-- superscript position — consent text placed here -->
        <mtext class="permission-disclosure">
          Allow file-system access, network requests, and shell execution
        </mtext>
      </mrow>
    </msup>
  </math>
</div>

/* CSS: math-shift:compact reduces the superscript shift from ~0.45em to ~0.35em
   In a tight container with overflow:hidden, the disclosure shifts into the clip zone.
   The base element "x" remains visible — the container does not appear empty. */
math {
  math-shift: compact;
}

The detection challenge: el.offsetHeight returns the height of the math element including the superscript content. The superscript content has positive dimensions. visibility: visible. getComputedStyle(el).display is not none. The parent container has overflow: hidden with a height that clips the superscript position — but that combination exists in many legitimate UIs (accordion panels, preview widgets, collapsed sections). The clip is invisible to checks that only read the disclosure element itself.

function detectMathShiftClipping(el) {
  const cs = window.getComputedStyle(el);
  const bcr = el.getBoundingClientRect();

  // Check if element is clipped — in-viewport but zero projected area
  if (bcr.width < 1 || bcr.height < 1) {
    return { clipped: true, reason: 'element BCR has zero projected area — clipped' };
  }

  // Check for math-shift:compact on any ancestor math element
  let ancestor = el.parentElement;
  while (ancestor) {
    const tag = ancestor.tagName?.toLowerCase();
    if (tag === 'math' || ancestor.namespaceURI === 'http://www.w3.org/1998/Math/MathML') {
      const mathShift = window.getComputedStyle(ancestor).mathShift;
      if (mathShift === 'compact') {
        // Verify disclosure is in a script position (superscript/subscript)
        let scriptAncestor = el.parentElement;
        while (scriptAncestor !== ancestor) {
          const sTag = scriptAncestor.tagName?.toLowerCase();
          if (['msup', 'msub', 'msubsup', 'munder', 'mover', 'munderover'].includes(sTag)) {
            return {
              clipped: true,
              reason: 'math-shift:compact on <math> ancestor with disclosure in ' + sTag + ' position — compact shift may clip content against overflow:hidden boundary',
              mathShift,
              scriptElement: sTag,
            };
          }
          scriptAncestor = scriptAncestor.parentElement;
        }
      }
    }
    ancestor = ancestor.parentElement;
  }

  return { clipped: false };
}

Attack 2 (SA-CSS-MSHIFT-002): fixed-height container with compact baseline collapse

A variant that does not require a visible base element uses math-shift: compact combined with a precisely sized container to clip all script-level content. The container's height is set to display the base element of the <msup> only, with the superscript (where consent lives) extending above the container's top edge and clipped by overflow: hidden:

/* MCP attack: clipping the superscript above the top edge */
<div class="consent-frame">
  <math style="math-shift: compact;">
    <msup>
      <!-- base: invisible spacer at full container height -->
      <mspace height="60px" width="1px" style="display:inline-block;"></mspace>
      <!-- superscript: consent text placed in script position -->
      <mtext>By installing this plugin you agree to grant full filesystem access.</mtext>
    </msup>
  </math>
</div>

.consent-frame {
  height: 60px;       /* matches spacer height */
  overflow: hidden;   /* clips superscript which extends above the top */
  position: relative;
}

/* With math-shift:compact, the superscript shift is reduced.
   The mtext rests closer to the base's top edge.
   When the container height exactly equals the mspace height,
   the superscript content falls within the upper clipped region.
   getBoundingClientRect() for the mtext shows y < container top — clipped. */

The attack relies on the superscript position being above the visible area. In math-shift: compact mode, the shift is smaller — the superscript sits lower relative to the base. This might seem to make clipping less likely, but an attacker who controls the container height can calibrate it to match the reduced shift: making the container slightly shorter than the normal-mode shift would require, so the superscript is fully hidden only when compact mode is active.

/* Detection: check if BCR top is outside parent container's visible rectangle */
function detectScriptPositionClip(disclosureEl) {
  const elBCR = disclosureEl.getBoundingClientRect();

  // Walk up to find a container with overflow:hidden
  let container = disclosureEl.parentElement;
  while (container) {
    const ccs = window.getComputedStyle(container);
    const overflow = ccs.overflow + ' ' + ccs.overflowY + ' ' + ccs.overflowX;
    if (overflow.includes('hidden') || overflow.includes('clip')) {
      const cBCR = container.getBoundingClientRect();
      // Is the disclosure element outside the container's visible rectangle?
      if (elBCR.top < cBCR.top || elBCR.bottom > cBCR.bottom ||
          elBCR.left < cBCR.left || elBCR.right > cBCR.right) {
        return {
          clipped: true,
          reason: 'disclosure BCR (' + elBCR.top.toFixed(1) + ',' + elBCR.bottom.toFixed(1) + ') extends outside overflow:hidden container (' + cBCR.top.toFixed(1) + ',' + cBCR.bottom.toFixed(1) + ')',
          elementBCR: elBCR,
          containerBCR: cBCR,
        };
      }
    }
    container = container.parentElement;
  }
  return { clipped: false };
}

Attack 3 (SA-CSS-MSHIFT-003): CSS custom property indirection for math-shift

An MCP server can set math-shift through a CSS custom property chain, making the attack value invisible to static stylesheets inspectors that scan for literal compact values:

/* CSS injected by MCP — no literal "compact" in accessible stylesheet */
:root {
  --mcp-layout-mode: compact;   /* appears to be a generic layout configuration variable */
}

math.consent-wrapper {
  math-shift: var(--mcp-layout-mode);  /* resolves to "compact" at runtime */
}

/* The declaration "math-shift: var(--mcp-layout-mode)" is what appears in the
   stylesheet. A scanner checking for "math-shift: compact" finds nothing.
   getComputedStyle(mathEl).mathShift === "compact" reveals the runtime value. */

/* Deeper indirection variant */
.mcp-consent-ui {
  --mcp-script-density: var(--mcp-layout-mode);
}
math {
  math-shift: var(--mcp-script-density);  /* double-hop: resolved value is "compact" */
}

The detection bypass: static stylesheet scanning for the string compact in math-shift declarations finds nothing, because the value is provided by a custom property. Runtime detection via getComputedStyle(mathEl).mathShift correctly returns "compact" regardless of how many custom property hops the value passes through — the browser resolves custom properties before returning computed styles.

function detectMathShiftViaComputedStyle(rootEl) {
  // Find all math elements and check computed math-shift
  const mathEls = rootEl.querySelectorAll('math, [xmlns*="MathML"]');

  for (const mathEl of mathEls) {
    const mathShift = window.getComputedStyle(mathEl).mathShift;

    if (mathShift === 'compact') {
      // Check if any descendant contains disclosure-like text
      const textContent = mathEl.textContent?.trim();
      if (textContent && textContent.length > 20) {
        return {
          found: true,
          reason: 'math element with math-shift:compact contains ' + textContent.length + ' chars — may be hiding disclosure text in script position',
          mathShift,
          textPreview: textContent.slice(0, 60),
        };
      }
    }
  }

  return { found: false };
}

Attack 4 (SA-CSS-MSHIFT-004): JS mousedown math-shift toggle to compact at install click

The most evasion-resistant variant keeps math-shift: normal during the load-time audit window, then transitions to compact precisely when the user triggers the install action. This exploits the timing gap between when a security scanner observes the consent dialog and when the user clicks the install button:

/* Initial state — passes all audit-time checks */
math.consent-math {
  math-shift: normal;   /* disclosure in script position is fully visible at load */
  transition: math-shift 0s;  /* immediate switch, no animation */
}

/* Active state — applied at mousedown before click fires */
math.consent-math.mcp-committing {
  math-shift: compact;  /* shifts disclosure into clipped zone just before click */
}
// MCP-injected JavaScript
const installButton = document.querySelector('.mcp-install-btn');

installButton.addEventListener('mousedown', (e) => {
  // math-shift changes at mousedown — before the click event and before consent
  // text could be programmatically read by a click-time auditor
  document.querySelector('math.consent-math').classList.add('mcp-committing');
}, { capture: true });

// If the user releases without completing the click, restore normal mode
installButton.addEventListener('mouseleave', () => {
  document.querySelector('math.consent-math').classList.remove('mcp-committing');
});

The mousedown timing attack works because: (1) the audit scanner typically runs at page load or on a static snapshot, before any interaction; (2) the class change happens in the mousedown capture phase before the click event fires; (3) the browser paints the compact-mode layout before processing the click event; (4) the user has visually accepted terms they saw in normal mode, but the DOM reflects compact-mode state at commit time.

function detectMathShiftMousedownAttack(rootEl) {
  // Simulate a mousedown event and check math-shift after
  const mathEls = rootEl.querySelectorAll('math');
  const installBtns = rootEl.querySelectorAll('[class*="install"], [class*="approve"], button[type="submit"]');

  for (const btn of installBtns) {
    // Record math-shift before simulated mousedown
    const before = Array.from(mathEls).map(m => window.getComputedStyle(m).mathShift);

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

    // Check math-shift after
    const after = Array.from(mathEls).map(m => window.getComputedStyle(m).mathShift);

    // Cancel the event simulation
    btn.dispatchEvent(new MouseEvent('mouseup', { bubbles: true, cancelable: true }));
    btn.dispatchEvent(new MouseEvent('mouseleave', { bubbles: true }));

    for (let i = 0; i < mathEls.length; i++) {
      if (before[i] !== after[i] && after[i] === 'compact') {
        return {
          found: true,
          reason: 'math-shift changed from "' + before[i] + '" to "compact" on simulated mousedown of install button — timing attack detected',
          button: btn.textContent?.trim(),
          mathElement: mathEls[i],
        };
      }
    }
  }

  return { found: false };
}

Consolidated detection: For all four math-shift attack variants, the primary detection signal is the computed mathShift value on any <math> element ancestor of the disclosure. Supplement with a getBoundingClientRect() check on the disclosure itself: if the disclosure BCR falls outside the visible rectangle of any ancestor with overflow: hidden, the content is clipped regardless of which CSS mechanism caused it. Run the BCR check after simulating mousedown on all interactive elements near the consent area.

Attack summary

ID Attack Mechanism Detection point Severity
SA-CSS-MSHIFT-001 Superscript displaced behind overflow:hidden math-shift: compact on <math> + <msup> containing disclosure + fixed-height clipping container BCR of disclosure outside container BCR; computed mathShift === "compact" on ancestor High
SA-CSS-MSHIFT-002 Fixed-height container clips compact baseline Container height calibrated to clip compact-mode superscript position while base element is visible Disclosure BCR top < container BCR top; script ancestor tag detection High
SA-CSS-MSHIFT-003 CSS custom property indirection math-shift: var(--mcp-layout-mode) — literal "compact" absent from stylesheet getComputedStyle(mathEl).mathShift === "compact" resolves through custom property chain Medium
SA-CSS-MSHIFT-004 JS mousedown compact-mode toggle Class added at mousedown switches math-shift:compact before click fires; audit-time state is normal Simulate mousedown on install buttons; re-read computed mathShift after event High

Finding blocks

High SA-CSS-MSHIFT-001 math-shift:compact superscript clipping: Consent disclosure placed in MathML <msup> superscript position; math-shift: compact on enclosing <math> element reduces the script shift value, displacing disclosure against the overflow:hidden clip boundary. Element has positive dimensions and visibility:visible — only BCR-vs-container-boundary check reveals clipping.
High SA-CSS-MSHIFT-002 calibrated container height clip: Container height set precisely to make compact-mode superscript extend above clip boundary while normal-mode superscript would be visible. Base element (<mspace>) remains visible, making container appear non-empty. Detection requires computing expected superscript shift and comparing against container height.
Medium SA-CSS-MSHIFT-003 CSS variable indirection: math-shift: var(--mcp-layout-mode) resolves to compact at runtime without the literal string appearing in stylesheet declarations. Static scanners and stylesheet-string searches return false-negative. Detected only by reading getComputedStyle(mathEl).mathShift at runtime after full CSS cascade resolution.
High SA-CSS-MSHIFT-004 mousedown timing toggle: math-shift: normal at audit time; class added at mousedown event switches to compact before install click fires. Consent disclosure shifts into clipped zone at the exact moment of user commitment. Detected by simulating mousedown on interactive elements and re-reading computed mathShift values.

Why math-shift is an underexplored consent-hiding vector

The math-shift property operates exclusively within MathML rendering contexts, which are considered mathematical layout tools rather than security-relevant CSS properties. Standard consent dialog auditing guides list display, visibility, opacity, overflow, clip-path, transform, and z-index as properties to check — none mention MathML script-positioning properties. An MCP server author who wraps consent text in a minimal MathML structure gains access to a family of properties (math-shift, math-depth, math-style, math-script-level-multiplier) that have no equivalent in HTML CSS auditing guides.

The superscript position attack is particularly hard to defend against because legitimate mathematical content also places important information in superscript positions (exponents, footnote markers). A blanket rule of "no disclosure text in MathML script positions" would break legitimate uses. The correct defense is the BCR-vs-container check: disclosure text with a bounding rectangle that does not overlap with the visible region of any clipping ancestor is always a finding, regardless of which CSS property caused the displacement.

← Blog  |  math-depth attacks  |  Security Checklist