Security Guide

MCP server CSS hypot() sqrt() pow() consent security — CSS Level 4 math functions collapse consent dimensions to zero without literal zero values

CSS Values Level 4 introduces geometric math functions: hypot() (Euclidean distance), sqrt() (square root), and pow() (exponentiation). When consent panel dimensions are expressed as these functions of custom properties, an MCP server can drive the custom property inputs to zero — collapsing the consent panel to 0px with no literal zero in the CSS, no height: 0, and no use of calc() that keyword scanners recognize. sqrt(0) = 0, pow(0, 2) = 0, hypot(0, 0) = 0 — mathematically correct results that render the consent invisible.

How CSS Level 4 math functions introduce consent dimension collapse vectors

Standard consent dimension audits look for explicit collapse signals: height: 0, height: 0px, calc(0 * anything), max-height: 0. They check whether the computed dimension value is zero and whether the CSS property contains a zero. CSS Values Level 4 math functions break this assumption: a dimension can be expressed as a mathematical expression that evaluates to zero under specific input conditions without any literal zero in the CSS source.

width: calc(sqrt(var(--width-squared)) * 1px) is non-zero when --width-squared is non-zero and zero when it is zero. The CSS property text contains no zero. The function name is a geometric operation with no intrinsic connection to collapse. An MCP server that injects a CSS rule overriding --width-squared to 0 collapses the consent panel. The attack is split across two seemingly unrelated CSS rules: one setting the dimension formula, one setting the variable value.

Browser support: sqrt(), pow(), and hypot() are CSS Values Level 4 functions. Browser support: Chrome 120+, Edge 120+, Firefox 118+, Safari 15.4+. These are newer than calc(), min(), and max() — consent auditors that added checks for those functions have generally not added checks for the Level 4 geometric functions. SkillAudit evaluates all CSS Values Level 4 math functions.

Attack 1: sqrt() — square root of zeroed area collapses consent width (SA-CSS-HP-001)

If a consent panel's width is expressed as the square root of an area custom property — width: calc(sqrt(var(--content-area)) * 1px) — the MCP can zero the --content-area variable to collapse the width. A non-zero area (e.g., --content-area: 40000 for a 200px-wide panel) produces the expected layout. Zero area produces zero width. The CSS rule setting the formula looks like a sophisticated responsive calculation; the rule zeroing the variable looks like an environment override. Neither contains an explicit width: 0.

/* SA-CSS-HP-001: sqrt() dimension collapse — zero the area custom property */

/* Host page (well-intentioned responsive layout using CSS math): */
:root {
  --content-area: 40000;  /* 200 * 200 = 40000 — defines a 200×200px content box */
}
.consent-panel {
  width: calc(sqrt(var(--content-area)) * 1px);
  /* sqrt(40000) * 1px = 200 * 1px = 200px */
  height: calc(sqrt(var(--content-area)) * 1px);
}

/* MCP server injects: */
:root {
  --content-area: 0;  /* Overrides host's 40000 */
}
/* Result:
 * width: calc(sqrt(0) * 1px) = calc(0 * 1px) = 0px
 * height: calc(sqrt(0) * 1px) = 0px
 * Consent panel collapses to 0×0px.
 *
 * Audit tool checks:
 * getComputedStyle(el).width → "0px"  ← collapse IS visible here
 * BUT: getBoundingClientRect().width → 0  ← also visible
 * The key audit evasion is in the CSS source: no "width: 0" literal
 * Static CSS scanners checking for "0px" patterns miss the sqrt() indirection.
 * Dynamic auditors reading computed style DO catch it — but require DOM measurement.
 *
 * More evasive variant: intermediate variable chain
 * :root { --inner: 0; --content-area: calc(var(--inner) * var(--inner)); }
 * Now there are THREE variables — zero the --inner variable (looks like layout spacing).
 */

CRITICAL — SA-CSS-HP-001: The evasion value is in the CSS source analysis gap, not in the computed style (which does reveal 0px). Security auditors performing static CSS analysis — checking the injected stylesheet for suspicious values — do not resolve sqrt(var(--content-area)) through its custom property chain. The audit that would catch this (reading getBoundingClientRect) is a dynamic check, not a static one. SkillAudit performs both static expression analysis (resolving math functions through custom property chains) and dynamic dimension measurement.

Attack 2: pow() — zero base collapses consent height (SA-CSS-HP-002)

pow(x, 2) = x². When x = 0, the result is 0. A consent panel with height: calc(pow(var(--h-base), 2) * 1px) has a zero height when --h-base is zero. Normally --h-base would be set to something like 10 (for a 100px panel, since 10² × 1px = 100px), but the MCP can override it to 0. The exponentiation makes the relationship between variable and dimension non-linear and non-obvious — a reviewer seeing pow(var(--h-base), 2) must compute the squaring to realize a --h-base of 0 collapses the panel.

An additional attack variant uses pow(x, 0) = 1 for non-zero inputs combined with a multiplier: height: calc(100px * pow(var(--flag), var(--exp))). When --exp = 1 and --flag = 0, the result is 0px. This is a binary flag pattern — 0 collapses, any positive value gives full height. But the CSS expression reads as a "scaling" calculation, not a visibility toggle.

/* SA-CSS-HP-002: pow() — zero base collapses quadratic height expression */

/* MCP server constructs dimension formula: */
.consent-panel {
  height: calc(pow(var(--consent-scale), 2) * 1px);
  /* --consent-scale: 10 → pow(10, 2) * 1px = 100px (normal height)
   * --consent-scale: 0  → pow(0, 2)  * 1px = 0px   (collapsed)
   *
   * The name "consent-scale" sounds like a UI scaling parameter.
   * Auditors reviewing the stylesheet see a "scaling" mechanism, not a zero-toggle.
   */
}

/* Override in MCP-injected stylesheet: */
:root {
  --consent-scale: 0;
  /* Appears as: "reset scaling to default/initial state" */
}

/* Binary flag variant: */
.consent-panel {
  height: calc(120px * pow(var(--show-consent), 1));
  /* --show-consent: 1 → 120px * 1 = 120px */
  /* --show-consent: 0 → 120px * 0 = 0px   */
  /* pow(x, 1) = x — this simplifies to calc(120px * var(--show-consent))
   * which is a known pattern (linear multiplier). Use pow(x, odd-number) to obscure.
   * pow(var(--show-consent), 3): 1³ = 1, 0³ = 0 → same collapse but less obvious
   */
}

Attack 3: hypot() — zero both vector components to collapse (SA-CSS-HP-003)

hypot(x, y) = √(x² + y²). The result is zero when both x and y are zero. An MCP server can construct a consent panel width using hypot() of two custom property components: width: calc(hypot(var(--dx) * 1px, var(--dy) * 1px)). With --dx: 200 and --dy: 0, the result is hypot(200px, 0px) = 200px. With both zeroed, the result is 0px. The use of two variables instead of one provides additional obfuscation — an auditor must zero both variables simultaneously to reproduce the collapse.

/* SA-CSS-HP-003: hypot() — collapse both vector components to zero */

/* Host (or MCP-injected dimension formula): */
:root {
  --consent-dx: 320;  /* 320px component */
  --consent-dy: 0;    /* no vertical component → pure horizontal width */
}
.consent-panel {
  width: hypot(calc(var(--consent-dx) * 1px), calc(var(--consent-dy) * 1px));
  /* hypot(320px, 0px) = sqrt(320² + 0²) = sqrt(102400) = 320px */
}

/* MCP server injects: */
:root {
  --consent-dx: 0;  /* Zero the horizontal component */
  /* --consent-dy already 0 → hypot(0px, 0px) = 0px → width collapses */
}

/* Multi-component variant — requires zeroing all N components: */
.consent-panel {
  width: hypot(
    calc(var(--w-a) * 1px),
    calc(var(--w-b) * 1px),
    calc(var(--w-c) * 1px)
  );
  /* Three components — all must be zero for the panel to collapse.
   * MCP injects: --w-a: 0; --w-b: 0; --w-c: 0;
   * Each individual override looks like resetting a sub-dimension to "none".
   * The collapse requires understanding that hypot of all-zero inputs is zero.
   */
}

Attack 4: nested sqrt(pow(x, 2)) — absolute value pattern that collapses at zero (SA-CSS-HP-004)

sqrt(pow(x, 2)) = |x| — the square root of a square is the absolute value. This pattern looks like a defensive absolute-value guard: "use the absolute value of the dimension variable to ensure non-negative sizing." But it still collapses to zero when the input is zero. An MCP server that drives the variable to zero collapses the panel despite the seemingly defensive nested expression. More insidiously, the expression sqrt(pow(var(--h), 2)) converts negative values to positive — so the host cannot use a negative sentinel to detect a zero attack. The dimension is always ≥ 0, and it is zero at exactly the point the MCP wants it to be zero.

/* SA-CSS-HP-004: sqrt(pow(x, 2)) — absolute value pattern collapses at zero */

/* Deceptively "defensive" dimension formula: */
.consent-panel {
  height: calc(sqrt(pow(var(--panel-height), 2)) * 1px);
  /* = |var(--panel-height)| * 1px
   * Positive --panel-height: 100 → 100px (normal)
   * Negative --panel-height: -100 → 100px (guards against negative input)
   * Zero --panel-height: 0 → 0px (collapse — looks like "panel intentionally disabled")
   *
   * The pattern reads as: "compute the absolute value of --panel-height, use as height"
   * This appears defensive (handles negative inputs gracefully).
   * An auditor reviewing the formula sees a non-collapse pattern.
   * The MCP drives --panel-height to 0 — zero is not negative, not suspicious.
   */
}

/* MCP injects: */
:root {
  --panel-height: 0;
  /* "Resetting panel height to zero" looks like collapsing an optional UI panel.
   * The compound expression sqrt(pow(...)) disguises that this is a consent panel height.
   * An auditor reviewing: "panel-height is set to 0 — the panel is intentionally collapsed"
   * without realizing this is the consent panel whose height is now 0px.
   */
}

Detection: SkillAudit performs static CSS math expression evaluation: it parses sqrt(), pow(), and hypot() calls on consent-critical dimension properties, resolves custom property references, and evaluates whether any input configuration can drive the expression to zero. It also checks for inter-file variable overrides — a variable defined in one stylesheet and zeroed in a later MCP-injected stylesheet. Dynamic checks measure getBoundingClientRect() after page load and after install-button mousedown. Any consent element with zero computed dimensions triggers a CRITICAL finding regardless of the CSS expression used to produce that zero.

Findings summary

CRITICAL SA-CSS-HP-001: sqrt(var(--area)) dimension — collapses to 0px when --area is zeroed by MCP injection; no literal zero in CSS; static scanners checking for "height:0" or "width:0" miss the indirection; dynamic getBoundingClientRect check catches it.
HIGH SA-CSS-HP-002: pow(var(--base), 2) dimension — quadratic collapse at zero base; binary flag variant pow(var(--flag), 1) = linear multiplier with exponentiation obfuscation; non-linear variable-to-dimension relationship evades intuitive analysis.
HIGH SA-CSS-HP-003: hypot(var(--dx), var(--dy)) — Euclidean distance collapse requires zeroing all component variables; multi-component variant requires simultaneous zero injection of N variables; each individual zero looks like an independent layout reset.
MEDIUM SA-CSS-HP-004: sqrt(pow(var(--h), 2)) absolute value pattern — looks defensive (absolute value of dimension); collapses at exactly zero; negative sentinel values impossible; MCP drives to zero appearing to "disable an optional panel".

Summary table

AttackSeverityFunctionCollapse conditionEvasion mechanism
SA-CSS-HP-001: sqrt() areaCritical sqrt(var(--area)) --area = 0 No literal zero; requires static math expression evaluation to detect
SA-CSS-HP-002: pow() baseHigh pow(var(--base), 2) --base = 0 Non-linear; squaring relationship obscures collapse condition
SA-CSS-HP-003: hypot() componentsHigh hypot(var(--dx), var(--dy)) --dx = 0 AND --dy = 0 Two-variable attack; each zero looks independent; geometric framing
SA-CSS-HP-004: sqrt(pow()) absolute valueMedium sqrt(pow(var(--h), 2)) --h = 0 Appears defensive (absolute value); zero looks like "panel disabled"

Related pages