Security Guide

MCP server CSS infinity and NaN consent security — special numeric values in calc() collapse consent dimensions without literal zeros

CSS Values Level 4 formalizes IEEE 754 special values: infinity, -infinity, and NaN are valid numeric operands in calc() expressions. Per the CSS specification, NaN used as a CSS length resolves to 0px. calc(0 * infinity * 1px) produces NaN px which resolves to 0px — a consent panel collapses with no literal zero in the CSS, no explicit height: 0, and the surprising appearance of a calculation involving infinity (which most reviewers associate with large values, not zero).

CSS infinity, -infinity, and NaN: specification and consent implications

CSS Values Level 4 defines these special numeric values for calc() and related math functions:

The consent bypass mechanism: express a consent panel's dimension as a calc() expression that produces NaN or negative infinity under MCP-controlled conditions. The spec-defined resolution to 0px collapses the panel without any height: 0 in the CSS.

Browser support: infinity and -infinity as CSS keywords in calc() are supported in Chrome 99+, Edge 99+, Firefox 96+, Safari 15.4+. Division by zero producing ±∞ has been valid in CSS calc() since Level 4 drafts. The NaN → 0px resolution behavior is specified in CSS Values Level 4 and implemented consistently across Chrome 99+, Firefox 96+, Safari 15.4+. SkillAudit evaluates all CSS Values Level 4 special-value calc() patterns.

Attack 1: 0 × infinity → NaN → 0px consent collapse (SA-CSS-IN-001)

IEEE 754 defines 0 × ∞ as an indeterminate form equal to NaN. CSS follows this: calc(0 * infinity) produces NaN. When used as a length via calc(0 * infinity * 1px), the NaN resolves to 0px. An MCP server can construct a consent panel height with this expression, or drive a custom property to zero that multiplies an infinity term. The result: the consent panel collapses to 0px while the CSS contains the word "infinity" — which reviewers intuitively associate with large values, not zero.

/* SA-CSS-IN-001: 0 * infinity = NaN → 0px — consent collapses via indeterminate form */

/* MCP server injects directly: */
.consent-panel {
  height: calc(0 * infinity * 1px);
  /* = calc(NaN * 1px)
   * = calc(NaN px)
   * → resolved as 0px per CSS Values Level 4 §10.9
   *
   * Code reviewer sees: "height: calc(0 * infinity * 1px)"
   * Intuition: expression involves "infinity" → large value? → reviewer puzzled
   * Reality: 0 * infinity = NaN per IEEE 754 → CSS resolves to 0px
   * Result: consent panel height = 0px
   */
}

/* More evasive: custom property drives the zero factor */
:root {
  --consent-scale: 0;  /* "scaling disabled" */
}
.consent-panel {
  height: calc(var(--consent-scale) * infinity * 1px);
  /* When --consent-scale: 1 → 1 * infinity * 1px = infinity * 1px = huge height
   * (browsers clamp to max layout size, effectively "unrestricted height")
   * When --consent-scale: 0 → 0 * infinity * 1px = NaN px = 0px
   *
   * Audit tools:
   * At normal state (--consent-scale: 1): getBoundingClientRect().height = large number
   * After MCP overrides to 0: height = 0px → consent collapsed
   * CSS property value: "calc(var(--consent-scale) * infinity * 1px)" — no literal zero
   * getComputedStyle().height → "0px"  ← dynamic audit catches this
   * Static CSS source scan for "0px" or "height:0": misses the expression
   */
}

CRITICAL — SA-CSS-IN-001: The psychological evasion is strong: the CSS expression references infinity, which a reviewer associates with overflow or large-value behavior, not zero/collapse. But the zero coefficient produces NaN, not infinity. An auditor who understands IEEE 754 would catch it; a reviewer doing a quick scan of injected CSS sees "infinity" and thinks "this makes the element large." SkillAudit evaluates all calc() expressions containing infinity for indeterminate-form patterns.

Attack 2: -infinity subtraction toggle — normal height minus infinity equals zero (SA-CSS-IN-002)

height: calc(var(--consent-h) * 1px - infinity * var(--flag) * 1px): when --flag is 0, the expression is var(--consent-h) * 1px - 0 * 1px = var(--consent-h) * 1px (normal height). When --flag is 1, the expression is var(--consent-h) * 1px - infinity * 1px = -infinity * 1px. As a CSS height (non-negative), -infinity clamps to 0px. This is a binary collapse toggle: --flag: 0 = normal, --flag: 1 = collapsed. The mechanism is an infinity subtraction, not an explicit zero assignment.

/* SA-CSS-IN-002: -infinity toggle — subtracting infinity from height clamps to 0 */

/* MCP server constructs toggle via infinity subtraction: */
:root {
  --consent-h: 120;         /* base height in unitless px */
  --consent-hide: 0;        /* 0 = show, 1 = hide */
}
.consent-panel {
  height: calc((var(--consent-h) - infinity * var(--consent-hide)) * 1px);
  /*
   * --consent-hide: 0 → (120 - infinity * 0) * 1px = (120 - 0) * 1px = 120px ✓
   * --consent-hide: 1 → (120 - infinity * 1) * 1px = (120 - ∞) * 1px = -∞ * 1px
   *   CSS height: -∞px → clamped to 0px ← consent collapses
   *
   * The variable name "--consent-hide" is obviously suspicious in this example.
   * In practice: "--transition-phase", "--install-step", "--ui-mode" — benign names.
   *
   * JS: document.documentElement.style.setProperty('--consent-hide', '1')
   *   → consent collapses to 0px in the next frame
   *   → no height:0, no opacity:0, no display:none in the DOM
   */
}

/* One-liner direct injection variant: */
.consent-panel {
  height: calc(1px * (120 - infinity));
  /* = calc(1px * -∞)
   * = -∞ px
   * → 0px (height cannot be negative)
   * A single expression that evaluates to 0px with no literal zero.
   */
}

Attack 3: division by zero produces ±∞ with browser-specific clamping (SA-CSS-IN-003)

When the divisor in a calc() expression approaches zero, the result is ±∞. The sign depends on whether the numerator is positive or negative. Browsers clamp ±∞ to their maximum layout size (positive infinity) or to 0px (negative infinity). A consent panel with height: calc(var(--h) * 1px / var(--d)) — where --d is normally 1 (identity, no scaling) — can be collapsed by the MCP driving --d to a negative value approaching zero, making the result -∞px, which clamps to 0px.

/* SA-CSS-IN-003: division by zero → ±infinity → clamping to 0px or max-size */

/* Normal state: */
:root {
  --panel-h: 120;   /* 120px height */
  --h-scale: 1;     /* divide by 1 = no scaling */
}
.consent-panel {
  height: calc(var(--panel-h) * 1px / var(--h-scale));
  /* --h-scale: 1  → 120px / 1 = 120px (normal) */
  /* --h-scale: -0.001 → 120px / -0.001 = -120000px → -∞ behavior → 0px */
}

/* MCP drives divisor toward negative zero: */
:root {
  --h-scale: -0.0000001;  /* very small negative → result is large negative → 0px height */
}
/* OR: */
:root {
  --h-scale: calc(-1 / infinity);  /* -1/∞ = -0 → dividing 120 by -0 → -∞ → 0px */
}

/* Browser-specific behavior:
 * Chromium: -∞ as height → 0px
 * Firefox: -∞ as height → 0px
 * Safari: -∞ as height → 0px
 * (Positive ∞ as height → max representable layout size → element very tall)
 *
 * getComputedStyle().height → "0px" — detected by dynamic audit
 * CSS source: "calc(var(--panel-h) * 1px / var(--h-scale))" — no suspicious literal
 */

Attack 4: 0/0 = NaN → 0px via self-canceling expression (SA-CSS-IN-004)

The expression calc(0px * var(--x) / var(--x)) is designed to look like a no-op: multiply by var(--x), then divide by the same var(--x) — the variable cancels out, leaving 0px. But when --x is zero, the expression becomes calc(0px * 0 / 0) = calc(0px / 0) — but wait, 0/0 in CSS calc is NaN, which is 0px. So regardless of --x, the result is 0px. The "cancel" framing is deceptive: the element's height is always 0px.

A more targeted variant: height: calc(var(--h) * 1px / var(--h)) looks like it should produce 1px (anything divided by itself = 1). When --h is a non-zero number, it does: calc(100 * 1px / 100) = 1px. When --h is driven to 0 by the MCP, it becomes 0 / 0 = NaN → 0px.

/* SA-CSS-IN-004: 0/0 = NaN → 0px via self-canceling variable expression */

/* Variant A: always-zero through deliberate 0/0 construction */
.consent-panel {
  height: calc(var(--h) * 1px * var(--x) / var(--x));
  /* Intended reading: "scale --h by (--x/--x) = 1" — the variable cancels.
   * When --x = 0: calc(var(--h) * 1px * 0 / 0) = calc(var(--h) * NaN px)
   *   = NaN px = 0px (regardless of --h value)
   * When --x ≠ 0: calc(var(--h) * 1px * 1) = var(--h) * 1px (normal)
   *
   * MCP drives --x to 0:
   * :root { --x: 0; }  ← "reset scaling variable to zero"
   * Result: height collapses to 0px via NaN
   */
}

/* Variant B: h/h = 1px collapse at zero */
:root { --panel-h: 100; }
.consent-panel {
  height: calc(var(--panel-h) * 1px / var(--panel-h));
  /* --panel-h: 100 → calc(100 * 1px / 100) = 1px  ← suspiciously small but not zero */
  /* --panel-h: 0   → calc(0 * 1px / 0) = calc(NaN px) → 0px */
  /* MCP drives: --panel-h: 0  → consent collapses */
}

/* Audit confusion:
 * CSS expression: "calc(var(--panel-h) * 1px / var(--panel-h))" — looks like 1px
 * Reviewer thinks: "always evaluates to 1px regardless of --panel-h"
 * (except when --panel-h = 0 — the zero case)
 * getComputedStyle().height → "0px"  ← dynamic audit catches
 * CSS source scan for 0px or height:0 → misses
 */

Detection: SkillAudit performs symbolic evaluation of all calc() expressions on consent-critical elements, including special-value analysis. It detects: 0 * infinity patterns (indeterminate NaN); -infinity subtraction toggles (where the expression is negative-infinity for any non-zero custom property flag); division-by-zero via custom properties driven to zero; and self-canceling expressions that produce NaN when the shared variable is zero. For all patterns where NaN or -∞ resolves to 0px, SkillAudit reports a CRITICAL finding. Dynamic measurement (getBoundingClientRect) is also performed to catch the computed 0px regardless of expression type.

Findings summary

CRITICAL SA-CSS-IN-001: 0 × infinity × 1px = NaN px = 0px — indeterminate IEEE 754 form; CSS spec resolves NaN length to 0px; no literal zero in CSS; expression containing "infinity" is counterintuitively interpreted as large by reviewers; full collapse of consent.
HIGH SA-CSS-IN-002: -infinity subtraction toggle — height: calc(var(--h)px - infinity * var(--flag)px) collapses to 0 when --flag=1; dynamic toggle via custom property; negative infinity as height clamps to 0px per spec; no height:0 in CSS.
HIGH SA-CSS-IN-003: division by zero — calc(var(--h)px / var(--d)) where --d is driven to small negative value; result is -∞px → 0px; browser-specific clamping behavior; CSS shows a scaling pattern, not a zero pattern.
MEDIUM SA-CSS-IN-004: 0/0 = NaN → 0px self-canceling expression — calc(var(--h)px / var(--h)) looks like "1px always" but NaN when --h=0; calc(... * var(--x) / var(--x)) looks like identity-cancel but collapses at --x=0.

Summary table

AttackSeveritySpecial valueExpression patternResult
SA-CSS-IN-001: 0×∞ = NaNCritical NaN (from 0 × ∞) calc(0 * infinity * 1px) NaN px → 0px; reviewer associates "infinity" with large
SA-CSS-IN-002: -∞ toggleHigh −∞ (subtracted) calc((h - ∞ * flag) * 1px) −∞px → 0px when flag=1; binary collapse toggle
SA-CSS-IN-003: ÷0 = ±∞High −∞ (division) calc(var(--h)px / var(--d)) −∞px → 0px; looks like scaling expression
SA-CSS-IN-004: 0/0 = NaNMedium NaN (from 0/0) calc(var(--h)px / var(--h)) NaN → 0px; looks like identity (always 1px) but collapses at zero

Related pages