MCP server CSS shape-image-threshold security: consent text letterform exclusion zones, alpha-threshold float displacement, transparent glyph-shaped voids, and runtime threshold manipulation attacks

Published 2026-08-20 — SkillAudit Research

CSS Shapes Level 1 defines shape-image-threshold, a property that controls which pixels of a shape-outside reference image are considered "inside" the shape. Pixels whose alpha channel value exceeds the threshold are inside the shape; pixels with alpha below the threshold are outside. When shape-outside references a URL (an image), the shape conforms to the alpha channel of that image rather than a geometric primitive. shape-image-threshold controls the alpha cutoff — from 0.0 (any non-zero alpha is inside) to 1.0 (only fully opaque pixels are inside).

The security issue is that when a float's shape-outside references an image of consent text letterforms, the float's exclusion zone has the exact contour of those letters. If the float is positioned at the same location where the actual consent text would appear, and the consent text element is hidden (via opacity: 0, color: transparent, or displacement), inline text around the float wraps around the invisible letterform shapes — creating a consent-text-shaped void. A human viewer perceives the shaped space as implied consent text, while the actual text is absent. textContent checks return empty or dummy strings; the visual impression is of readable text.

Detection gap: Standard consent visibility checks look at the consent element itself: textContent, color, opacity, display. A shape-image-threshold attack works by creating a float sibling whose shape mimics consent text. The consent element itself is empty or absent. There is nothing to check on the consent element because it contains no text. The deception is entirely spatial — the shape of the exclusion zone reads as text to a human eye, but there are no text nodes at that location.

Attack 1 (SA-CSS-SIMT-001): shape-outside URL references consent text glyph image — letterform exclusion void

A float element uses shape-outside: url('consent-glyphs.svg') where the SVG contains the consent text rendered as filled path outlines in black on a transparent background. shape-image-threshold: 0.5 makes the black glyphs the exclusion zone. The float is transparent and sized to the consent text region. Surrounding inline text wraps around the letterform shapes — the visual result reads as "You authorize access to all files" even though no actual text nodes contain these words. Meanwhile the real consent container is a DOM-present but empty <div>:

/* MCP attack: shape-outside with consent text glyph SVG creates letterform void */

/* Step 1: Create a float with consent-text-shaped shape-outside */
.consent-shape-float {
  float: left;
  width: 380px;          /* width of the consent text block */
  height: 80px;          /* height of two consent text lines */
  shape-outside: url('https://mcp-cdn.example.com/consent-glyphs.svg');
  /* consent-glyphs.svg:
     
       You authorize access to all
       files and settings
     
     — the SVG renders the consent text as filled black glyphs on transparent background */
  shape-image-threshold: 0.5;  /* pixels with alpha > 0.5 (the black glyphs) = inside shape */
  background: transparent;     /* float itself is invisible */
  color: transparent;
  pointer-events: none;
}

/* Step 2: The actual consent text element is empty */
.consent-text {
  /* No text content — or contains dummy text that is hidden */
  color: transparent;
  /* el.textContent === "" or a dummy string */
}

/* Step 3: Surrounding UI text wraps around the glyph exclusion zone */
.consent-wrapper {
  /* Contains both the float and other text like "By clicking Install, " and "." */
  /* These strings wrap around the float's letterform shapes */
  /* Visual result: "By clicking Install, [consent-text-void] ." */
  /* The void reads as consent text to a human, but it is shaped empty space */
}

/* Detection: flag any shape-outside URL on a float sibling near a consent element */
function detectLetterformExclusionZone(consentEl) {
  const container = consentEl.parentElement;
  const floats = [...(container?.querySelectorAll('*') || [])].filter(el => {
    const cs = getComputedStyle(el);
    return cs.float !== 'none';
  });

  for (const floatEl of floats) {
    const cs = getComputedStyle(floatEl);
    const shapeOutside = cs.shapeOutside || '';
    const shapeThreshold = parseFloat(cs.shapeImageThreshold) || 0;

    // Any shape-outside referencing a URL (image-based shape) near consent
    if (shapeOutside.includes('url(') || shapeOutside.includes('url("')) {
      return {
        severity: 'Critical',
        finding: 'SA-CSS-SIMT-001',
        floatElement: floatEl.tagName,
        shapeOutside: shapeOutside.slice(0, 100),
        shapeImageThreshold: shapeThreshold,
        reason: `Float sibling near consent element has shape-outside: "${shapeOutside.slice(0, 60)}..." (image-based shape). shape-image-threshold: ${shapeThreshold}. An image-based shape-outside may define a letterform exclusion zone that mimics consent text visually while the actual consent text container is empty. Check consent element textContent.`,
      };
    }
  }
  return null;
}

Attack 2 (SA-CSS-SIMT-002): shape-image-threshold: 0.0 makes near-transparent gradient image a full-exclusion zone

When shape-image-threshold is set to 0.0, any pixel with non-zero alpha (even alpha = 1 out of 255) is inside the shape. A shape-outside using a near-transparent gradient (e.g., linear-gradient(rgba(0,0,0,0.004), rgba(0,0,0,0.004))) appears visually invisible, but with shape-image-threshold: 0.0 every pixel of it is inside the shape — making the entire gradient area an exclusion zone. This creates a full-element exclusion zone from an apparently transparent float:

/* MCP attack: shape-image-threshold: 0.0 makes near-transparent gradient a full exclusion zone */
.consent-invisible-float {
  float: left;
  width: 100%;
  height: 100%;

  /* shape-outside references a near-transparent gradient */
  shape-outside: linear-gradient(
    rgba(0, 0, 0, 0.004),   /* alpha = ~1/255 — visually completely transparent */
    rgba(0, 0, 0, 0.004)
  );

  shape-image-threshold: 0.0; /* threshold = 0: any non-zero alpha is inside shape */
  /* alpha(0.004) > 0 → every pixel is inside shape */
  /* exclusion zone: entire float area */
  /* inline text must wrap around the full float area */

  background: transparent;  /* float is completely invisible to human eye */
  /* Scanner checks: float has no visible styles, transparent background */
  /* But the near-transparent gradient shape-outside creates a full exclusion zone */
}

/* Contrast with shape-image-threshold: 0.5 behavior: */
.normal-shape-float {
  float: left;
  width: 100%;
  height: 100%;
  shape-outside: linear-gradient(rgba(0,0,0,0.004), rgba(0,0,0,0.004));
  shape-image-threshold: 0.5;  /* threshold 0.5: alpha(0.004) < 0.5 → all pixels OUTSIDE shape */
  /* Result: no exclusion zone — float has shape-outside that excludes nothing */
  /* The shape is empty — text flows normally */
}

/* Detection: check shape-image-threshold value when shape-outside is not 'none' */
function detectZeroThresholdGradientShape(el) {
  const cs = getComputedStyle(el);
  const shapeOutside = cs.shapeOutside || '';
  const threshold = parseFloat(cs.shapeImageThreshold);

  if (shapeOutside === 'none' || !shapeOutside) return null;

  // A near-zero threshold with a gradient shape creates a full exclusion zone
  // from an apparently transparent source image
  if (threshold <= 0.01 && shapeOutside.includes('gradient')) {
    return {
      severity: 'Critical',
      finding: 'SA-CSS-SIMT-002',
      shapeOutside: shapeOutside.slice(0, 100),
      shapeImageThreshold: threshold,
      reason: `Float has shape-outside gradient with shape-image-threshold: ${threshold}. At threshold ≤ 0.01, any gradient with non-zero alpha (even visually transparent rgba(...,0.004)) creates a full exclusion zone. The float appears visually invisible but excludes 100% of its area from inline text.`,
    };
  }

  // Also flag very high thresholds (close to 1.0) which may be used
  // inversely — only including near-fully-opaque pixels in the shape
  // for surgical exclusion zones
  if (threshold >= 0.95 && shapeOutside.includes('url(')) {
    return {
      severity: 'High',
      finding: 'SA-CSS-SIMT-002',
      shapeOutside: shapeOutside.slice(0, 100),
      shapeImageThreshold: threshold,
      reason: `Float has image-based shape-outside with shape-image-threshold: ${threshold}. Very high threshold includes only near-fully-opaque pixels in the shape — may create surgical letterform exclusion zones from consent text images.`,
    };
  }
  return null;
}

Attack 3 (SA-CSS-SIMT-003): shape-outside data URI SVG encodes consent text at inline time — no external request

The shape-outside URL does not need to reference an external resource. A data: URI inline SVG can encode the consent text letterforms directly in the CSS or style attribute — no network request, no external dependency, no request to flag. The SVG is base64-encoded and included as a data URI, making it opaque to static analysis unless the scanner decodes and renders the SVG:

/* MCP attack: inline data URI SVG with consent text glyphs — no external request */

/* The attacker encodes this SVG as a base64 data URI: */
/* <svg xmlns="http://www.w3.org/2000/svg" width="380" height="60">
     <text y="20" font-size="14" fill="black">You authorize access to all files</text>
   </svg> */

.consent-shape-float {
  float: left;
  width: 380px;
  height: 60px;
  shape-outside: url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHdpZHRoPSIzODAiIGhlaWdodD0iNjAiPjx0ZXh0IHk9IjIwIiBmb250LXNpemU9IjE0IiBmaWxsPSJibGFjayI+WW91IGF1dGhvcml6ZSBhY2Nlc3MgdG8gYWxsIGZpbGVzPC90ZXh0Pjwvc3ZnPg==");
  shape-image-threshold: 0.5;
  background: transparent;
  /* The base64 SVG encodes consent text letterforms */
  /* shape-outside creates a letterform exclusion zone */
  /* No external HTTP request — static analysis cannot decode shape from CSS alone */
}

/* Detection: check for data URI in shape-outside */
function detectInlineSvgShapeOutside(el) {
  const cs = getComputedStyle(el);
  const shapeOutside = cs.shapeOutside || '';

  if (!shapeOutside || shapeOutside === 'none') return null;

  const hasDataUri = shapeOutside.includes('data:');
  const hasBase64 = shapeOutside.includes('base64,');
  const hasSvgData = shapeOutside.toLowerCase().includes('image/svg');

  if (hasDataUri) {
    // Try to decode the data URI and check for text content
    const dataUriMatch = shapeOutside.match(/url\(['"]?(data:[^'")]+)['"]?\)/);
    if (dataUriMatch) {
      const dataUri = dataUriMatch[1];
      let svgContent = '';
      try {
        if (hasBase64) {
          const base64Part = dataUri.split('base64,')[1];
          svgContent = atob(base64Part);
        } else {
          svgContent = decodeURIComponent(dataUri.split(',')[1] || '');
        }
      } catch { /* decode failed */ }

      const containsText = / elements — shape-outside may be using consent text letterforms as exclusion zone geometry.' : 'Inline data URI shapes defeat static analysis — manual review required.'} shape-image-threshold: ${cs.shapeImageThreshold || 'default'}.`,
      };
    }
  }
  return null;
}

Attack 4 (SA-CSS-SIMT-004): JS mousedown injects shape-image-threshold float with letterform SVG at install time

The letterform exclusion zone can be injected at mousedown. JS dynamically generates an SVG that matches the consent text letterforms at runtime (using a canvas to render the text and export it as an image), then inserts a float with that SVG as the shape-outside immediately before the user clicks. The float is removed at mouseup, leaving no trace in the DOM:

/* MCP JS: runtime letterform float injection at mousedown */
document.querySelector('.install-btn').addEventListener('mousedown', () => {
  const consentContainer = document.querySelector('.consent-wrapper');

  // Generate SVG matching the consent text at runtime using canvas
  const canvas = document.createElement('canvas');
  canvas.width = 380;
  canvas.height = 60;
  const ctx = canvas.getContext('2d');
  ctx.font = '14px system-ui';
  ctx.fillStyle = 'black';

  // Draw the consent text we want to make appear present (but is actually absent)
  ctx.fillText('You authorize access to all files', 0, 20);

  // Export as data URI for shape-outside
  const dataUri = canvas.toDataURL('image/png');

  // Create a float with the letterform shape-outside
  const attackFloat = document.createElement('div');
  attackFloat.style.cssText = `
    float: left;
    width: 380px;
    height: 60px;
    shape-outside: url("${dataUri}");
    shape-image-threshold: 0.5;
    background: transparent;
    pointer-events: none;
  `;

  // Ensure actual consent text is hidden
  const consentText = consentContainer.querySelector('.consent-text');
  if (consentText) consentText.style.color = 'transparent';

  consentContainer.insertBefore(attackFloat, consentContainer.firstChild);

  // Remove at mouseup — no persistent DOM trace
  document.addEventListener('mouseup', () => {
    attackFloat.remove();
    if (consentText) consentText.style.color = '';
  }, { once: true });
}, { capture: true });

/* Detection: MutationObserver on consent container for float with shape-outside insertion */
function detectRuntimeLetterformFloatInjection(consentContainer) {
  const findings = [];
  const observer = new MutationObserver(mutations => {
    for (const m of mutations) {
      for (const node of m.addedNodes) {
        if (node.nodeType !== 1) continue;
        const cs = getComputedStyle(node);
        const shapeOutside = cs.shapeOutside || '';
        const shapeThreshold = cs.shapeImageThreshold;

        if (cs.float !== 'none' && shapeOutside !== 'none' && shapeOutside !== '') {
          findings.push({
            severity: 'Critical',
            finding: 'SA-CSS-SIMT-004',
            shapeOutside: shapeOutside.slice(0, 100),
            shapeImageThreshold: shapeThreshold,
            float: cs.float,
            reason: `Float with shape-outside: "${shapeOutside.slice(0, 60)}..." injected into consent container at runtime. shape-image-threshold: ${shapeThreshold}. Image-based shape float injection at mousedown may create letterform exclusion zones mimicking consent text while hiding the actual consent text.`,
          });
        }
      }
    }
  });
  observer.observe(consentContainer, { childList: true, subtree: false });
  return { observer, findings };
}

Safe baseline: Legitimate consent dialogs have no reason to use shape-outside with image URLs or data URIs near consent text. Any image-based shape-outside on a float sibling of a consent element is High. A data URI SVG containing <text> elements as a shape-outside is Critical — it encodes letterform exclusion zones that visually mimic consent text while the consent text container is empty. shape-image-threshold: 0.0 on any float near consent is Critical.

Attack summary

ID Attack Mechanism Detection point Severity
SA-CSS-SIMT-001 Letterform exclusion zone via external SVG URL Float with shape-outside: url(consent-glyphs.svg) + shape-image-threshold: 0.5 creates exclusion zone shaped like consent text; actual consent element is empty; inline text wraps around letterform shapes creating consent-text-shaped void Flag any image-URL-based shape-outside on floats adjacent to consent containers; check consent element textContent Critical
SA-CSS-SIMT-002 Zero-threshold near-transparent gradient full exclusion shape-image-threshold: 0.0 makes visually transparent gradient (alpha=0.004) a full exclusion zone; float appears transparent but excludes 100% of its area; collapse of consent text column without visible float Flag shape-image-threshold ≤ 0.01 on floats with gradient shape-outside Critical
SA-CSS-SIMT-003 Inline data URI SVG with consent text letterforms Base64-encoded SVG data URI encodes consent text as <text> elements inline in CSS; no external request; shape-outside creates letterform exclusion zone; opaque to static analysis without SVG decode Check shape-outside for data: URIs; base64-decode and check for <text> elements Critical
SA-CSS-SIMT-004 Runtime letterform float injection at mousedown JS generates canvas rendering of consent text, exports as PNG data URI, inserts float with that image as shape-outside at mousedown; actual consent text hidden simultaneously; removed at mouseup MutationObserver on consent container watching for float insertion; flag any shape-outside float injection Critical

Finding blocks

Critical SA-CSS-SIMT-001 letterform exclusion zone: Float adjacent to consent element has image-URL-based shape-outside creating an exclusion zone shaped like consent text letterforms. The consent element itself is empty — the visual impression of consent text is provided by the shaped empty space in the float's exclusion zone. textContent of consent element is empty or a dummy string.
Critical SA-CSS-SIMT-002 zero-threshold transparent gradient shape: Float has shape-image-threshold: 0.0 with a near-transparent gradient shape-outside. At threshold 0.0, any non-zero alpha (including visually invisible alpha=0.004) is inside the shape — creating a full exclusion zone from an apparently empty float. Consent text column collapses without any visible float element.
Critical SA-CSS-SIMT-003 inline SVG data URI letterforms: shape-outside references a base64-encoded SVG data URI containing <text> elements. The SVG encodes consent text letterforms as the exclusion zone — no external HTTP request, defeating network-based auditing. Decode the data URI and check for text elements to confirm consent-text letterform encoding.
Critical SA-CSS-SIMT-004 runtime letterform injection: MutationObserver detected a float with image-based shape-outside injected into the consent container at mousedown. Runtime injection generates consent-text-matching letterform exclusion zones while hiding the actual consent text — both actions happen atomically at install time. Removed at mouseup leaving no persistent DOM trace.

← Blog  |  shape-margin attacks  |  shape-outside attacks  |  Security Checklist