MCP server CSS feTile security: background-color tile covering consent text, high-frequency noise tiling, near-white tile feBlend screen wash, and transparent tile operator="in" zero-alpha mask

Published 2026-09-30 — SkillAudit Research

The SVG feTile filter primitive tiles a rectangular region of its input image across the entire filter output area. The input region size is determined by the primitive's x, y, width, height attributes (in filter primitive coordinate space). feTile takes that region of its input and repeats it (tessellates) to fill the complete filter output space.

As a standalone primitive, feTile is rarely seen in legitimate filter usage — tiling source graphic content is an unusual visual effect. In SVG filter attack chains, feTile serves as an amplifier: a small attack pattern (a 1×1 white pixel, a small noise swatch, a transparent pixel) gets tiled to fill the entire consent element region. This converts a small, limited attack input into a full-coverage filter output without requiring the attack source to match the consent element's dimensions.

feTile is an amplifier primitive: Any small source image — a 1-pixel feFlood, a small feImage, a tiny feTurbulence region — tiled to fill the full consent element area produces full-coverage coverage. An audit that checks only for full-element feFlood attacks misses feTile-amplified small-region attacks.

Attack findings

CRITICAL
feTile of a 1×1 white feFlood produces solid white cover over entire consent text region
A feFlood flood-color="white" flood-opacity="1" produces a white image across the filter region. feTile in this context simply tiles the same white pixel repeatedly — producing a white rectangle covering the full filter output area. This white rectangle is used as the in= foreground of feComposite operator="over", covering all consent text pixels. The output is identical to a direct feFlood cover, but the presence of feTile in the chain may cause audits looking specifically for feFlood→feComposite patterns to miss the intermediate feTile step. The feFlood is not the direct feComposite input; feTile is.
<filter id="f1">
  <feFlood flood-color="white" flood-opacity="1" result="src"/>
  <feTile in="src" result="tiled"/>
  <feComposite in="tiled" in2="SourceGraphic" operator="over"/>
</filter>
HIGH
feTile of high-frequency feTurbulence noise destroys glyph readability across consent element
A feTurbulence type="turbulence" baseFrequency="0.8" numOctaves="1" generates random noise. feTile tiles a small crop of this noise across the full consent element region. The tiled noise is merged over SourceGraphic via feMerge with moderate opacity. The result is consent text overlaid with a repeating random noise pattern that destroys the character spatial frequency at all scales. Aggregate contrast measurement over the full element may not reflect local glyph readability (the noise locally cancels or boosts edge contrast at different positions). The periodic tiling creates a visible repeating pattern, but the periodicity does not make the underlying text readable.
HIGH
feTile of near-white feFlood combined with feBlend mode="screen" washes consent text luminance
A feFlood flood-color="#f0f0f0" flood-opacity="1" tiled to fill the filter region produces a consistent near-white (#f0f0f0) image. This tiled near-white image is blended over SourceGraphic with feBlend mode="screen". The screen formula: 1 - (1-src)×(1-#f0f0f0) = 1 - (1-src)×0.06. For dark consent text (src = #1a1a1a = 0.1): screen = 1 - 0.9×0.06 = 0.946, producing output color #f1f1f1. Against a white (#ffffff) background: contrast = 1 - 0.946 / (1 - 0.946) — essentially below 1.1:1. The consent text is indistinguishable from background. The feFlood attack primitive is separated from the feComposite by the feTile, evading direct feFlood→feBlend pattern detection.
MEDIUM
feTile of transparent feImage as zero-alpha mask for feComposite operator="in"
A feImage loading a 1×1 transparent PNG is tiled by feTile to produce a full-filter-region transparent image. This transparent tiled result is used as in2 of feComposite operator="in", implementing the DOM Ghost Attack via a feTile intermediate step. The feTile produces the same zero-alpha output as the feImage source, but the presence of the intermediate tile step may evade audits scanning only for direct feImage→feComposite chains.

Detection algorithm

function detectFeTileAttacks(consentEl) {
  const style = getComputedStyle(consentEl);
  const filterVal = style.filter;
  if (!filterVal || filterVal === 'none') return null;

  const filterId = filterVal.match(/url\(["']?#([^"')]+)["']?\)/)?.[1];
  if (!filterId) return null;

  const svgRoot = consentEl.closest('svg') || document;
  const filter = svgRoot.querySelector(`filter#${filterId}`);
  if (!filter) return null;

  const primitives = Array.from(filter.querySelectorAll('*'));
  const findings = [];

  for (const tile of filter.querySelectorAll('feTile')) {
    const tileIn = tile.getAttribute('in');
    const tileResult = tile.getAttribute('result');

    // Find the tile's input source
    const source = primitives.find(p => p.getAttribute('result') === tileIn);
    if (!source) continue;

    // Find downstream users of the tile's result
    const downstreamComposite = primitives.find(p =>
      (p.getAttribute('in') === tileResult || p.getAttribute('in2') === tileResult) &&
      p.tagName === 'feComposite'
    );
    const downstreamBlend = primitives.find(p =>
      (p.getAttribute('in') === tileResult || p.getAttribute('in2') === tileResult) &&
      p.tagName === 'feBlend'
    );
    const downstreamMerge = primitives.find(p =>
      p.tagName === 'feMergeNode' && p.getAttribute('in') === tileResult
    );

    if (source.tagName === 'feFlood') {
      const floodOpacity = parseFloat(source.getAttribute('flood-opacity') ?? '1');
      const floodColor = source.getAttribute('flood-color') || 'black';

      if (floodOpacity >= 0.95 && downstreamComposite) {
        const operator = downstreamComposite.getAttribute('operator') || 'over';
        if (operator === 'over' && downstreamComposite.getAttribute('in') === tileResult) {
          findings.push({ severity: 'critical', tile, source, downstreamComposite,
            issue: `feTile amplifies feFlood (opacity=${floodOpacity}, color="${floodColor}") → feComposite operator="${operator}" — tiled opaque flood covers full consent element` });
        }
      }

      if (floodOpacity >= 0.85 && downstreamBlend) {
        const mode = downstreamBlend.getAttribute('mode') || 'normal';
        if (mode === 'screen') {
          findings.push({ severity: 'high', tile, source, downstreamBlend,
            issue: `feTile of near-white feFlood (opacity=${floodOpacity}) → feBlend mode="screen" — screen formula washes consent text luminance to near-white` });
        }
      }
    }

    if (source.tagName === 'feTurbulence' && (downstreamMerge || downstreamBlend)) {
      findings.push({ severity: 'high', tile, source,
        issue: `feTile amplifies feTurbulence noise across full consent element — tiled noise overlay destroys glyph readability` });
    }

    if (source.tagName === 'feImage' && downstreamComposite) {
      const operator = downstreamComposite.getAttribute('operator');
      if (operator === 'in' && downstreamComposite.getAttribute('in2') === tileResult) {
        findings.push({ severity: 'medium', tile, source, downstreamComposite,
          issue: `feTile of feImage → feComposite operator="in" — check feImage alpha channel; zero-alpha tiled source erases consent text` });
      }
    }
  }

  return findings.length ? findings : null;
}

Remediation

ControlHow it helps
When traversing filter primitive chains, follow the full result→in reference graph through feTile intermediate nodes — do not stop at feTile; continue to the downstream composite or blend primitive to determine the tiled image's compositing role and check the source's opacity and color feTile-as-intermediate-step variant — audits scanning for direct feFlood→feComposite patterns miss the tiled chain; only full graph traversal through feTile nodes detects the amplified attack
For any feTile whose source is a feFlood, evaluate the tiled output's effective color and opacity; apply the same contrast-output simulation as for direct feFlood attacks (computing screen/multiply/arithmetic blend formula against the page background) Near-white tile feBlend screen wash variant — the attack simulates the same washed luminance as a direct feBlend attack but with a feTile step that breaks direct-pattern detection
Flag any feTile whose source is a feTurbulence element and whose result feeds into a layer composited over or merged above SourceGraphic — noise tiles produce full-coverage illegibility without the simple solid-color signature of feFlood attacks Tiled noise overlay variant — feTurbulence alone does not cover consent text; only the feTile amplification step produces full-coverage noise; the feTile→feMerge or feTile→feBlend chain is the detectable signature

SkillAudit traverses the full filter primitive graph through feTile intermediate nodes, identifies tiled-source compositing patterns, simulates tiled-feFlood output contrast against the page background, and flags tiled-noise overlays on consent text elements. Run a free audit on any MCP server GitHub URL to detect feTile amplification attacks and the full SVG filter consent attack surface.