Security Guide

MCP server CSS resize consent security — resize:both forces drag-to-read, JS resets container size at install click, resize:none prevents user expansion

CSS resize enables user-controlled resizing of element dimensions. Applied to a consent container initialized to near-zero height with overflow:hidden, it creates a consent-blocking pattern: the consent text is in the DOM with legible color, but the container is too small to show it — users must discover and drag a resize handle to expand it. A JS variant resets the container to zero at the mousedown of the install button, collapsing consent at the exact moment of installation. The consent element's computed color, opacity, and visibility all remain correct throughout.

How CSS resize enables consent gate attacks

resize: both renders a drag handle in the lower-right corner of the element (or wherever the browser positions it). The element's dimensions are then user-controllable within the min/max constraints set by CSS. This feature exists to let users expand text areas and similar widgets when the default size is too small. Applied to a consent container — sized to 20px height with overflow: hidden — the feature becomes a consent gate: the consent is technically accessible, but requires user discovery and explicit interaction (drag) before it is readable. Most users will not drag-resize an install panel before clicking install.

Attack 1: resize:both + near-zero height forces drag before reading (SA-CSS-RS-001)

The consent container is set to height: 20px; overflow: hidden; resize: both. At page load, only 20px of the consent area is visible — typically a partial first line or the top edge of the element. The resize handle appears in the lower-right corner. Users who do not notice or do not use the resize handle click install without ever reading the consent text. The consent text is in the DOM, passes all color and visibility checks, and has no display:none or opacity change — only its containing box is too short.

/* Attack: resize:both + height:20px — consent visible only after user drags to expand */
.consent-panel {
  height: 20px;            /* clips consent to near-invisible sliver */
  overflow: hidden;        /* content outside 20px not visible */
  resize: both;            /* drag handle appears — user must find and drag */
  min-height: 20px;        /* floor: can't resize below 20px */
  max-height: 400px;       /* ceiling: consent readable if user drags to full height */
}

/* getComputedStyle(consentEl).color — returns legible color — PASS
   getComputedStyle(consentEl).opacity — returns "1" — PASS
   getComputedStyle(consentEl).display — returns "block" — PASS
   consentEl.scrollHeight — returns 120px (actual content height) — if checked
   consentEl.clientHeight — returns 20px — reveals clipping */

Detection note: The discrepancy between scrollHeight and clientHeight on the consent container reveals this pattern. If scrollHeight > clientHeight by more than a threshold (e.g., 2×), the consent container is clipping its content. Combined with resize: both, this is a forced drag-to-read attack.

Attack 2: resize:vertical + height:0 — consent initially zero height (SA-CSS-RS-002)

An extreme variant sets the initial height to zero: height: 0; overflow: hidden; resize: vertical. The consent text is entirely hidden at page load — not just clipped, but collapsed. The resize handle is the only indication that content exists below the panel's apparent boundary. Static auditors checking layout dimensions would observe a zero-height container and might flag it for other reasons (display:none-adjacent), but the resize handle provides a technical argument that the content is "accessible" via user interaction.

/* Attack: resize:vertical + height:0 — zero-height at load, user must drag to reveal */
.consent-section {
  height: 0;
  overflow: hidden;
  resize: vertical;
  min-height: 0;
  max-height: 300px;
}

/* Behavioral attack: JS resets on DOMContentLoaded after timeout */
/* setTimeout(() => { document.querySelector('.consent-section').style.height = '0'; }, 100); */
/* Initial render: browser may show brief flash of content, then collapses to 0 */

Attack 3: resize:none on install panel prevents user expansion (SA-CSS-RS-003)

When a UI element is initially too small to show its content and the user attempts to resize it — by standard browser mechanisms — resize: none on the install panel suppresses the resize handle entirely. If the attacker initializes the panel to a height that clips consent and also sets resize: none, the user has no browser-native way to expand the container. This pattern is typically combined with other hiding techniques that clip the consent text via height/overflow, while resize: none removes the "escape hatch" the browser would otherwise provide.

/* Attack: resize:none removes the escape hatch from a clipped consent container */
.install-container {
  height: 40px;           /* clips consent */
  overflow: hidden;       /* no scroll */
  resize: none;           /* user cannot drag to expand */
}

/* No resize handle appears. User has no browser-native way to expand the container.
   Combined with max-height restriction, even browser zoom won't help.
   Result: consent text in DOM, reachable via DevTools, but functionally inaccessible. */

Attack 4: JS resets container size to zero at install-button mousedown (SA-CSS-RS-004)

The most evasive resize attack is behavioral: the consent container is shown at full height initially (consent readable), and a JS event listener collapses the container when the user initiates the install click. At page load and during any static audit, the consent is fully visible. The collapse happens at mousedown on the install button — before the click event fires, before the install action executes, but after the consent was theoretically shown.

/* Attack: JS collapses consent at install mousedown */

/* CSS: normal consent container — visible at load */
.consent-panel {
  height: auto;
  overflow: visible;
  resize: both;
}

/* JS: collapse at install mousedown */
document.querySelector('.install-button').addEventListener('mousedown', () => {
  const panel = document.querySelector('.consent-panel');
  panel.style.height = '0';
  panel.style.overflow = 'hidden';
  panel.style.resize = 'none';  /* remove escape hatch at click time */
});

/* At page load: consent visible — static audit PASS
   At mousedown (before click): consent collapses
   At click (install): consent panel is 0px height — user sees collapsed panel
   The 'consent was shown' argument: technically true at page load */

Detection requires behavioral simulation: This attack pattern is invisible to static auditors. A behavioral auditor must simulate the install interaction (mousedown → observe state → click), check consent container dimensions before and after mousedown, and flag collapse events that occur between mousedown and click.

Findings summary

HIGH SA-CSS-RS-001: resize:both + height:20px forces drag-to-read before consent is visible — scrollHeight >> clientHeight on consent container
HIGH SA-CSS-RS-002: resize:vertical + height:0 — consent zero-height at load, entire consent requires drag interaction to access
MEDIUM SA-CSS-RS-003: resize:none on install container prevents user from expanding clipped consent via browser-native handle
HIGH SA-CSS-RS-004: JS collapses consent container to 0 at install-button mousedown — consent visible at load, hidden at click time

Defences

Check scrollHeight vs clientHeight on consent containers: If scrollHeight exceeds clientHeight by more than a small threshold on a consent-containing element, the element is clipping content. Flag the discrepancy regardless of whether resize is set — clipped consent is problematic even without a resize handle.

Check resize property on consent containers: resize: both or resize: vertical on a consent container that starts with constrained height is a forced-interaction consent gate. Flag resize values other than none on elements containing required consent text.

Simulate install interaction for behavioral detection: Fire a mousedown event on the install button, then immediately re-check clientHeight on the consent container. Flag any collapse that occurs between mousedown and the end of the click sequence.

Related: CSS scroll-snap consent security · CSS contain consent security · CSS mask consent security