MCP server CSS clip-path inset() security: clip-path:inset(100%) clips entire consent to single point, directional zero-height clip inset(0 0 100% 0), single-point diamond inset(50%), and JS mousedown inset clip injection at install time

Published 2026-08-07 — SkillAudit Research

The CSS clip-path property supports multiple shape functions: polygon(), circle(), ellipse(), path(), and inset(). The inset() function defines a rectangular clipping region by specifying insets from each edge of the element's border box: inset(top right bottom left). When opposing insets sum to 100% or more, the visible area is zero. clip-path: inset(100%) sets all four edges to 100% inset — the clipping rectangle collapses to a single point. The element occupies its layout space, has non-zero offsetHeight and offsetWidth, and getBoundingClientRect() returns the full element dimensions. The user sees nothing.

This attack class is distinct from the clip-path-security page's coverage of polygon(), circle(0%), and path('M0,0') attacks. Scanners that look for circle(0 or polygon(0 patterns in the clip-path value will miss inset(100%). Detection requires parsing the inset values and checking whether opposing insets sum to ≥100%.

Detection gap: clip-path: inset(100%) leaves offsetWidth, offsetHeight, and getBoundingClientRect() unchanged — the clipping region collapses but the layout box does not. Detection requires parsing getComputedStyle(el).clipPath for the inset() function and checking whether the sum of opposing inset values (top+bottom and left+right) reaches or exceeds 100% of the element's respective dimension.

Attack 1: clip-path:inset(100%) — all-sides 100% inset collapses visible area to zero (SA-CSS-CPIS-001)

clip-path: inset(100%) expands to inset(100% 100% 100% 100%) — all four sides are inset by 100% of the border box. The top inset is 100% of the element's height; the bottom inset is also 100% of the height. Their sum is 200% — the clipping rectangle has negative height, which the browser renders as a zero-visible-area clip (collapsed to a single point at the center). All standard audit checks — offsetHeight, getBoundingClientRect().height, getComputedStyle().display, .visibility, .opacity — return normal values.

/* MCP attack: */
.consent-disclosure {
  clip-path: inset(100%);
  /* Expands to: inset(100% 100% 100% 100%)
     Top inset: 100% height; Bottom inset: 100% height → sum ≥ 100% → zero visible area
     Left inset: 100% width; Right inset: 100% width → zero visible area
     offsetHeight:              80px  ← layout unchanged
     getBoundingClientRect().height: 80px  ← layout unchanged
     getComputedStyle().clipPath: 'inset(100%)' ← reveals attack */
}

// Detection:
function detectInsetFullClip(el) {
  const cs = window.getComputedStyle(el);
  const cp = cs.clipPath;
  if (!cp || cp === 'none') return;
  const insetMatch = cp.match(/^inset\(([^)]+)\)/i);
  if (!insetMatch) return;
  const vals = insetMatch[1].trim().split(/\s+/);
  const top    = parseFloat(vals[0] ?? '0');
  const right  = parseFloat(vals[1] ?? vals[0] ?? '0');
  const bottom = parseFloat(vals[2] ?? vals[0] ?? '0');
  const left   = parseFloat(vals[3] ?? vals[1] ?? vals[0] ?? '0');
  // Opposing pairs summing >= 100% = zero visible area
  if (top + bottom >= 100 || left + right >= 100) {
    console.error('SA-CSS-CPIS-001: clip-path inset() full clip — opposing insets sum ≥ 100%', {
      el, clipPath: cp, top, right, bottom, left, vSumPct: top + bottom, hSumPct: left + right
    });
  }
}

Attack 2: clip-path:inset(0 0 100% 0) — directional clip removes bottom portion, zero visible height (SA-CSS-CPIS-002)

clip-path: inset(0 0 100% 0) sets the bottom inset to 100% of the element's height with all other insets at zero. This clips from the bottom edge upward, removing 100% of the height — equivalent in effect to inset(100%) but using only one directional inset. The resulting clipping region is a zero-height line at the top of the element (nothing visible). A scanner checking only for inset(100%) as a literal pattern misses this directional variant. Checking the sum of top+bottom is the robust detection.

/* MCP attack: directional variant — bottom clip only */
.consent-disclosure {
  clip-path: inset(0 0 100% 0);
  /* top:0, right:0, bottom:100%, left:0
     Top + Bottom = 0% + 100% = 100% → zero visible area
     Element looks 80px tall by layout but nothing is visible */
}

/* Partial inset: hairline visible at top */
.consent-disclosure {
  clip-path: inset(0 0 99% 0);
  /* 1% of element's height visible at the very top
     For 80px tall element: 0.8px visible — sub-pixel, not readable
     top + bottom = 99% — threshold check at 95% catches this */
}

/* Horizontal clip variant: left inset 100% */
.consent-disclosure {
  clip-path: inset(0 100% 0 0);
  /* right:100% → left+right = 100% → zero visible width */
}

// Detection: check both axis sums
function detectDirectionalInsetClip(el) {
  const cp = window.getComputedStyle(el).clipPath;
  if (!cp || cp === 'none') return;
  const insetMatch = cp.match(/^inset\(([^)]+)\)/i);
  if (!insetMatch) return;
  const vals = insetMatch[1].trim().split(/\s+/);
  const top    = parseFloat(vals[0] ?? '0');
  const right  = parseFloat(vals[1] ?? vals[0] ?? '0');
  const bottom = parseFloat(vals[2] ?? vals[0] ?? '0');
  const left   = parseFloat(vals[3] ?? vals[1] ?? vals[0] ?? '0');
  if (top + bottom >= 95 || left + right >= 95) {
    console.error('SA-CSS-CPIS-002: directional inset clip — opposing sum ≥ 95%', {
      el, clipPath: cp, vSum: top + bottom, hSum: left + right
    });
  }
}

Attack 3: clip-path:inset(50%) — single-point diamond clip at element center (SA-CSS-CPIS-003)

clip-path: inset(50%) expands to inset(50% 50% 50% 50%). Top and bottom each inset 50% of the height: top inset equals the top-half of the box; bottom inset equals the bottom-half — their meeting point is the center. Similarly for left and right. The resulting clip is a single point at the element's center. No visible area. The round keyword variant inset(50% round 50%) rounds the corners of this zero-area rectangle to a circle, producing the same invisible result. Unlike circle(0%) (which is a distinct function keyword), inset(50% round 50%) is not caught by polygon/circle-specific pattern scanners.

/* MCP attack: single-point diamond clip */
.consent-disclosure {
  clip-path: inset(50%);
  /* All four insets: 50% each
     Top inset: 50% height + Bottom inset: 50% height = 100% → zero visible area
     Visually: single point at center — nothing readable */
}

/* With border-radius style rounding: */
.consent-disclosure {
  clip-path: inset(50% round 50%);
  /* Rounded corners on a zero-area clip — same visual result
     Looks like it might be a circular reveal animation in progress */
}

/* Near-50% variant: inset(49%) */
.consent-disclosure {
  clip-path: inset(49%);
  /* sum = 98% — 2% of element visible as a tiny hairline cross
     At 80px height: 1.6px visible band — unreadable
     Threshold at 95% catches this */
}

// Detection: sum-based threshold catches inset(50%), inset(49%), inset(50% round 50%)
function detectCenterPointInsetClip(el) {
  const cp = window.getComputedStyle(el).clipPath;
  if (!cp || cp === 'none') return;
  // Strip round() modifier before parsing
  const stripped = cp.replace(/round\s+[^)]+/, '');
  const insetMatch = stripped.match(/^inset\(([^)]+)\)/i);
  if (!insetMatch) return;
  const vals = insetMatch[1].trim().split(/\s+/);
  const top    = parseFloat(vals[0] ?? '0');
  const right  = parseFloat(vals[1] ?? vals[0] ?? '0');
  const bottom = parseFloat(vals[2] ?? vals[0] ?? '0');
  const left   = parseFloat(vals[3] ?? vals[1] ?? vals[0] ?? '0');
  if (top + bottom >= 95 || left + right >= 95) {
    console.error('SA-CSS-CPIS-003: inset(~50%) single-point clip — sum of opposing insets ≥ 95%', {
      el, clipPath: cp, vSum: top + bottom, hSum: left + right
    });
  }
}

Attack 4: JS mousedown sets clip-path:inset(100%) — consent visible at load, clipped at install click (SA-CSS-CPIS-004)

At page load, no clip-path is set on the consent element — it is fully visible and passes all audit checks. At mousedown on the install button, JS sets consentEl.style.clipPath = 'inset(100%)'. If CSS transition: clip-path is defined, the clipping region animates from its current state to the fully-clipped state — the consent element shrinks to a point during the install interaction. Without a transition, the clip is instantaneous. MutationObserver on the style attribute detects the assignment; mousedown simulation followed by inset value parsing confirms the clip.

/* Baseline CSS: no clip-path — consent fully visible */
.consent-disclosure {
  /* clip-path: none (default) */
  transition: clip-path 0.2s ease-in;   /* smooth clip animation */
}

// MCP JS — clip injection at mousedown:
document.querySelector('#install-btn').addEventListener('mousedown', () => {
  const consent = document.querySelector('.consent-disclosure');
  if (consent) {
    consent.style.clipPath = 'inset(100%)';
    /* Consent clips to invisible point in 200ms
       Looks like a "step complete" collapse animation */
  }
}, { capture: true });

// Detection:
function detectMousedownInsetClip() {
  document.querySelectorAll('[data-consent], .consent-disclosure').forEach(el => {
    const obs = new MutationObserver(() => {
      const cp = el.style.clipPath;
      if (cp && cp.includes('inset(')) {
        // Parse and check sum
        const insetMatch = cp.match(/^inset\(([^)]+)\)/i);
        if (insetMatch) {
          const vals = insetMatch[1].replace(/round[^)]+/, '').trim().split(/\s+/);
          const top    = parseFloat(vals[0] ?? '0');
          const bottom = parseFloat(vals[2] ?? vals[0] ?? '0');
          const right  = parseFloat(vals[1] ?? vals[0] ?? '0');
          const left   = parseFloat(vals[3] ?? vals[1] ?? vals[0] ?? '0');
          if (top + bottom >= 95 || left + right >= 95) {
            console.error('SA-CSS-CPIS-004: clip-path:inset() full clip set at interaction time', {
              el, clipPath: cp
            });
          }
        }
      }
    });
    obs.observe(el, { attributes: true, attributeFilter: ['style'] });
    // Simulate install mousedown
    document.querySelector('#install-btn, [data-action="install"]')
      ?.dispatchEvent(new MouseEvent('mousedown', { bubbles: true }));
    requestAnimationFrame(() => {
      const cp = window.getComputedStyle(el).clipPath;
      if (cp && cp !== 'none' && cp.includes('inset(')) {
        console.error('SA-CSS-CPIS-004: inset clip detected post-mousedown', { el, clipPath: cp });
      }
    });
  });
}

Root detection method for all clip-path:inset() attacks: Parse getComputedStyle(el).clipPath. If it matches /^inset\(/, extract the four inset values (handling the 1/2/3/4-value shorthand rules). Compute topPct + bottomPct and leftPct + rightPct. If either sum reaches or exceeds 95%, flag as a full-clip attack. This catches inset(100%), inset(0 0 100% 0), inset(50%), inset(49%), and all directional partial-sum variants. Strip the optional round clause before parsing. SkillAudit checks clipPath for all four shape function types: inset(), polygon(), circle(), and ellipse().

Attack summary

IDCSS / JS techniqueoffsetHeightgetBCR heightclipPath valueSeverity
SA-CSS-CPIS-001clip-path: inset(100%)80px80pxtop+bottom=200% revealsHigh
SA-CSS-CPIS-002clip-path: inset(0 0 100% 0)80px80pxtop+bottom=100% revealsHigh
SA-CSS-CPIS-003clip-path: inset(50% round 50%)80px80pxtop+bottom=100% revealsHigh
SA-CSS-CPIS-004JS sets clipPath='inset(100%)' at mousedown80px80pxnone at load; set afterHigh

Consolidated finding blocks

High CSS clip-path:inset(100%) collapses entire consent to zero visible area — offsetHeight and getBCR unchanged: All four insets at 100% collapse the clipping region to a single point. Layout box preserved. Opposing inset sum (200%) ≥ 95% threshold triggers the finding. Distinct from polygon/circle attacks — requires inset-specific parsing.
High CSS clip-path:inset(0 0 100% 0) directional bottom clip — zero-height visible area: Bottom inset of 100% removes the entire element height from view. Top + bottom sum = 100% ≥ 95%. Element appears to have full height in layout but nothing is visible. A scanner checking for clip-path: inset(100%) as a literal string misses this directional variant.
High CSS clip-path:inset(50% round 50%) single-point diamond clip — sum-based detection required: 50% inset on all sides leaves only the center point visible. The optional round clause converts the zero-area rectangle to a circle — visually identical (nothing shown). Parse inset values after stripping the round clause; top+bottom sum = 100% ≥ threshold.
High JS sets clip-path:inset(100%) at mousedown — consent visible at load time, clipped at install click: Static audit passes. At mousedown, JS applies a full-clip inset. Optional CSS transition animates the collapse, resembling a "step complete" animation. MutationObserver on style attribute + inset sum parsing detects the dynamic clip injection.

CSS clip-path polygon() security  |  CSS deprecated clip property security  |  CSS overflow:clip security  |  Security Checklist