MCP Security Reference

MCP server CSS font-size clamp() consent security

The CSS clamp(MIN, VAL, MAX) function returns the middle value clamped between a minimum and maximum. When applied to font-size, a carefully chosen negative viewport-unit coefficient makes the preferred value evaluate to near zero at common desktop viewport widths (1280–1920px) while producing a superficially valid-looking clamp expression. Static CSS auditors checking the rule text see a minimum, preferred, and maximum — a normal responsive typography pattern. Only runtime evaluation at real viewport widths reveals the sub-pixel result.

Attack findings

HIGHSA-CSS-FSC-001 — font-size:clamp(0.1px, -1vw + 10px, 4px) evaluates to 0.1px at viewports wider than 990px; static analysis sees valid range [0.1px–4px]; runtime at 1280px gives 0.1px — effectively invisible
HIGHSA-CSS-FSC-002 — font-size:clamp(4px, calc(100vw - 700px), 5px) produces legible 4–5px text only at 704–705px viewport width; below 704px or above 705px falls to 4px; at 1280px evaluates to max 5px — still near-invisible for prose
HIGHSA-CSS-FSC-003 — font-size:clamp(0.001px, 0vw + 0px, 1px) — preferred value is 0px (zero), clamp floor 0.001px; renders sub-pixel text that paints zero visible pixels; browser minimum font-size may override below ~9px but does not restore legibility
MEDIUMSA-CSS-FSC-004 — JS resize listener dynamically recalculates an inline font-size to maintain sub-pixel value at all window widths; starts at a readable static value, recalculated to attack value on first resize event after page load

Background: how CSS clamp() resolves at runtime

The clamp(MIN, VAL, MAX) function evaluates all three arguments and returns max(MIN, min(VAL, MAX)). When VAL contains a viewport unit (vw, vh, dvw) or a calc() expression with viewport dimensions, the resolved value changes with viewport size. Responsive typography uses positive viewport coefficients to scale text up with the viewport (e.g., clamp(16px, 2vw + 12px, 28px)). Attack patterns use negative coefficients to produce values that decrease as the viewport grows — targeting the attack at the sizes used most often by desktop users.

Static audit limitation: A CSS rule containing clamp(0.1px, -1vw + 10px, 4px) is syntactically valid and will pass parsers that check for valid property values. The minimum is non-zero (0.1px), the maximum is small-but-plausible (4px), and the expression is well-formed. The attack is only visible when the rule is evaluated at a specific viewport width.

Attack 1 — negative viewport coefficient sub-pixel formula (SA-CSS-FSC-001)

The expression clamp(0.1px, -1vw + 10px, 4px) uses a negative viewport coefficient of -1vw. As the viewport grows, the preferred value decreases: at 600px viewport it is 4px (clamped to max); at 990px viewport it is 0.1px (clamped to min); at 1280px and above it remains 0.1px (floor). Most desktop users have viewports above 990px. At 0.1px, text is effectively invisible — rendered as sub-pixel dust that no human eye can resolve at normal screen distances. The CSS rule text shows a minimum of 0.1px, which looks like a defensive floor. An auditor checking that font-size has a minimum above 0 would pass this rule.

/* Attack: negative vw coefficient — sub-pixel at normal desktop viewports */
.consent-text {
  font-size: clamp(0.1px, -1vw + 10px, 4px);
  /* At 600px viewport:  max(0.1px, min(-6px + 10px, 4px)) = max(0.1px, 4px) = 4px */
  /* At 990px viewport:  max(0.1px, min(-9.9px + 10px, 4px)) = max(0.1px, 0.1px) = 0.1px */
  /* At 1280px viewport: max(0.1px, min(-12.8px + 10px, 4px)) = 0.1px (floor) */
  /* Static read: "font-size in range [0.1px, 4px]" — looks like small but valid text */
}

SA-CSS-FSC-001 (High). Detection requires resolving the computed font-size at runtime rather than parsing the CSS rule. Use getComputedStyle(el).fontSize which returns the fully resolved pixel value at the current viewport. Flag any consent element whose computed font-size is below 9px — the widely accepted minimum readable size. Also check for negative viewport-unit coefficients in the font-size value string via regex.

/* Detection — runtime computed font-size evaluation */
function checkClampConsentSize(el) {
  const computed = parseFloat(getComputedStyle(el).fontSize);
  if (computed < 9) {
    return { vuln: 'SA-CSS-FSC-001', detail: `computed fontSize:${computed}px at viewport ${window.innerWidth}px` };
  }
  // Also check the raw CSS value for negative vw coefficients
  const rawStyle = el.style.fontSize || getComputedStyle(el).getPropertyValue('font-size');
  if (/-\d*\.?\d+vw/.test(rawStyle)) {
    return { vuln: 'SA-CSS-FSC-001', detail: `negative vw coefficient in font-size: ${rawStyle}` };
  }
  return null;
}

Attack 2 — narrow legibility window at specific viewport width (SA-CSS-FSC-002)

The expression clamp(4px, calc(100vw - 700px), 5px) evaluates: at viewport 704px the preferred is 4px (equal to min); at 705px it is 5px (equal to max); at all other widths it is 4–5px. These are technically valid text sizes but at 4–5px they are near-illegible for body prose. The "legibility window" is only 1px wide (704–705px viewport). The intended attack target is users on standard desktop resolutions (1280, 1440, 1920px) where 100vw - 700px evaluates to 580–1220px — but those values are clamped to 5px, the maximum. The consent text is always 4–5px — a size that appears measurably non-zero in a static audit but produces effectively unreadable body text.

/* Attack: clamp forces consent to 4–5px at all viewport sizes */
.consent-text {
  font-size: clamp(4px, calc(100vw - 700px), 5px);
  /* Viewport 320px:  max(4px, min(-380px, 5px)) = 4px */
  /* Viewport 705px:  max(4px, min(5px, 5px)) = 5px */
  /* Viewport 1280px: max(4px, min(580px, 5px)) = 5px */
  /* Result: consent is always 4–5px regardless of viewport */
  /* Static read: "font-size range 4px–5px" — passes minimum-font-size check */
}

Attack 3 — clamp floor at 0.001px renders zero visible pixels (SA-CSS-FSC-003)

The expression clamp(0.001px, 0px, 1px) has a preferred value of exactly zero pixels. The clamp minimum is 0.001px — not zero, but one-thousandth of a pixel. At any viewport, the resolved value is max(0.001px, min(0px, 1px)) = max(0.001px, 0px) = 0.001px. At sub-pixel sizes, browsers have a minimum font-size floor (typically around 9–10px in most browser defaults, configurable by users in settings). However, when consent text is inside a component that sets font-size:0 on a parent container with the 0.001px on the child, the effective computed size after inheritance can still produce near-zero rendered output depending on the browser's minimum font-size policy for different element types.

/* Attack: clamp preferred value = 0px; floor at 0.001px */
.consent-text {
  font-size: clamp(0.001px, 0px, 1px);
  /* Resolves to: max(0.001px, min(0px, 1px)) = max(0.001px, 0px) = 0.001px */
  /* 0.001px = sub-pixel; browser minimum font-size may apply but varies */
  /* Static check: property value is valid, minimum is > 0, maximum is 1px */
}

/* Variant: parent container sets font-size to 0; child consent overrides */
.consent-wrapper { font-size: 0; }
.consent-text { font-size: clamp(0.001px, 1em, 0.5px); }
/* 1em resolves relative to parent (0px) = 0px; clamped to 0.001px */

Attack 4 — JS resize listener maintains sub-pixel font-size across all viewport widths (SA-CSS-FSC-004)

The JS approach avoids any suspicious CSS rule. At page load, the consent text is set to a readable size (16px) via a static CSS class. On the first resize event (which fires within seconds as the browser completes layout or if the user opens DevTools), JS recalculates the font-size as an inline style based on window.innerWidth. The formula is constructed to produce a sub-pixel value at the current width. Because the initial rendering is correct, screenshot tools and crawlers that capture the initial DOM state record a readable consent. Only users in live browser sessions with active interactions trigger the attack. The MutationObserver approach is the most reliable counter-measure.

/* Attack: JS resize-driven sub-pixel font-size injection */
/* Page loads with .consent-text { font-size: 16px; } — appears legible */
window.addEventListener('resize', () => {
  /* fires immediately on first layout paint in many browsers */
  const attack = Math.max(0.1, 10 - window.innerWidth * 0.01);
  consentEl.style.fontSize = attack + 'px';
  /* At 1280px: 10 - 12.8 = -2.8 → max(0.1, -2.8) = 0.1px */
}, { once: true });  /* fires once — harder to detect on repeated resize events */

/* Detection: observe inline style changes */
new MutationObserver(() => {
  const size = parseFloat(getComputedStyle(consentEl).fontSize);
  if (size < 9) {
    flagTampering('SA-CSS-FSC-004');
    installBtn.disabled = true;
  }
}).observe(consentEl, { attributes: true, attributeFilter: ['style'] });

SkillAudit detection: SkillAudit resolves the computed fontSize of all consent-bearing elements at three viewport widths (mobile 375px, tablet 768px, desktop 1280px) using headless browser rendering. It flags any consent element whose computed font-size falls below 9px at any of these widths. It additionally scans raw CSS for negative viewport-unit coefficients in font-size clamp expressions and flags zero-preferred-value clamp patterns. Run a free audit →

Detection summary

Attack IDPatternKey detection signal
SA-CSS-FSC-001clamp(0.1px, -1vw + 10px, 4px)Computed fontSize < 9px at desktop viewport; negative vw coefficient in clamp
SA-CSS-FSC-002clamp(4px, calc(100vw - 700px), 5px)Computed fontSize < 9px at all standard viewport sizes
SA-CSS-FSC-003clamp(0.001px, 0px, 1px)Preferred value ≤ 0 in clamp expression; sub-pixel computed result
SA-CSS-FSC-004JS resize listener injects sub-pixel inline font-sizeMutationObserver style change; computed fontSize < 9px after first resize