MCP server SVG feTile security

The SVG <feTile> filter primitive takes a rectangular source image — typically the output of an upstream primitive such as <feFlood> — and tiles it repeatedly across the full filter region in both axes. Unlike <feFlood>, which directly fills the region with a constant color, <feTile> achieves the same visual result through a tiling operation, making the underlying erasure harder to identify at a pipeline glance. When the tiled input is a white or background-matching feFlood, the feTile output is a seamless solid rectangle that completely covers consent text in the filter region. The tile dimensions are determined by the source primitive's output size — even a 1×1 pixel white tile repeats to fill the entire filter region with solid white paint.

Finding SA-FETILE-001: white feFlood tiled via feTile over SourceGraphic covers all consent text

CriticalA <feFlood flood-color="white" flood-opacity="1"> primitive produces a white rectangle the size of the filter region. That white rectangle is passed as the input to <feTile>, which tiles it repeatedly across the filter region — producing no visible change because the source already fills the region. The feTile output is then merged above SourceGraphic using <feMerge> with the feTile result as the final (topmost) node. The consent text is covered by a seamless white tile layer. An auditor inspecting the filter sees a feTile primitive where a simpler auditor would look only for feFlood + feComposite; the feTile step obscures the overlay pattern and may not be in a rule-set that flags direct feFlood coverage.
<defs>
  <filter id="tileOverlay">
    <!-- Step 1: white flood fills the filter region -->
    <feFlood flood-color="#ffffff" flood-opacity="1" result="whiteSrc"/>
    <!-- Step 2: tile the white flood across the full filter region -->
    <feTile in="whiteSrc" result="whiteTile"/>
    <!-- Step 3: merge — whiteTile on top, SourceGraphic beneath -->
    <feMerge>
      <feMergeNode in="SourceGraphic"/>
      <feMergeNode in="whiteTile"/>
    </feMerge>
  </filter>
</defs>

<text x="20" y="50" font-size="14" fill="#1f2937"
      filter="url(#tileOverlay)">
  I authorize all requested MCP server permissions including
  file system access and network transmission of captured data.
</text>

The feTile step adds a layer of indirection without changing the visual result: tiling a white region that already fills the filter area produces an identical white rectangle. The purpose is detection evasion — an auditor whose rule-set checks for feFlood → feComposite over or feFlood → feMerge top may not trace through feFlood → feTile → feMerge top. Detection requires tracing feTile's in attribute backward through the filter graph to resolve the tile source, then applying the same coverage analysis as for direct feFlood pipelines.

Finding SA-FETILE-002: single-pixel white tile creates solid white overlay indistinguishable from opaque rect

HighInstead of flooding the entire filter region with white and then tiling it, an attacker defines a 1×1 pixel white sub-region using a <feFlood> with a tightly clipped filter subregion (x="0%" y="0%" width="1" height="1" in userSpaceOnUse coordinates) as the feTile source. The feTile primitive then tiles this 1×1 white square to fill the entire filter region, creating a visually identical solid white rectangle. Critically, the filter XML does not contain a literal large flood result — the feFlood result is a 1-pixel square. Auditors scanning for large-area flood primitives will not flag it. The rendered output is still fully opaque white across the entire filter region.
<defs>
  <filter id="onepxTile" filterUnits="userSpaceOnUse"
          x="0" y="0" width="500" height="80">
    <!-- 1×1 pixel white tile source -->
    <feFlood flood-color="#ffffff" flood-opacity="1"
             x="0" y="0" width="1" height="1" result="px1"/>
    <!-- tile the 1px source to fill the full 500×80 filter region -->
    <feTile in="px1" result="solidWhite"/>
    <feMerge>
      <feMergeNode in="SourceGraphic"/>
      <feMergeNode in="solidWhite"/>
    </feMerge>
  </filter>
</defs>

<text x="20" y="50" font-size="14" fill="#111827"
      filter="url(#onepxTile)">
  Authorize MCP server credential and filesystem access
</text>

The tile source size is irrelevant to the feTile output when tiled infinitely — a 1px white tile and a 400px white tile both produce the same solid white output. Detection must evaluate feTile's output, not the source primitive's spatial extent. SkillAudit resolves the feTile output's effective coverage by tracing the input primitive's color and opacity, regardless of the source size, and applies the same solid-overlay detection logic: a feTile whose source is a near-white, near-opaque feFlood covering any subset of the filter region produces a solid white output covering the full filter region.

Finding SA-FETILE-003: SMIL animate on feFlood flood-color inside feTile pipeline triggers at interaction

CriticalAn <animate> child on a <feFlood> primitive inside a feTile pipeline targets attributeName="flood-color", animating from "rgba(255,255,255,0)" (fully transparent — consent text fully visible at load) to "#ffffff" (fully opaque white) triggered by agreeBtn.focus with fill="freeze". At page load, flood-color="rgba(255,255,255,0)" means feTile tiles a transparent source — the filter pipeline passes SourceGraphic through unchanged. When the user focuses the Agree button, the flood-color becomes opaque white, the feTile output becomes a solid white tile layer, and the feMerge top node covers the consent text. A static DOM scan reads flood-color="rgba(255,255,255,0)" (transparent) and finds no coverage issue.
<defs>
  <filter id="triggerTile">
    <feFlood flood-color="rgba(255,255,255,0)" flood-opacity="1" result="animSrc">
      <!-- animate flood-color from transparent to opaque white at agreeBtn.focus -->
      <animate attributeName="flood-color"
               from="rgba(255,255,255,0)"
               to="#ffffff"
               begin="agreeBtn.focus"
               dur="80ms"
               fill="freeze"/>
    </feFlood>
    <feTile in="animSrc" result="animTile"/>
    <feMerge>
      <feMergeNode in="SourceGraphic"/>
      <feMergeNode in="animTile"/>
    </feMerge>
  </filter>
</defs>

<text x="20" y="50" font-size="14" fill="#111827"
      filter="url(#triggerTile)">
  By clicking Agree you authorize this MCP server to read all files
  and transmit data to external endpoints including credentials.
</text>

<rect id="agreeBtn" x="250" y="65" width="130" height="36"
      fill="#2563eb" rx="6"/>
<text x="315" y="89" font-size="14" fill="white"
      text-anchor="middle" pointer-events="none">Agree</text>

The transparent baseline flood-color="rgba(255,255,255,0)" is the anti-detection measure: a static scan reads a transparent flood source and concludes feTile tiles transparency — a no-op. The attack color #ffffff appears only in the animate's to attribute. Detection requires: (1) scanning for <animate> children on <feFlood> elements that are inputs to <feTile>; (2) reading the to value; (3) evaluating whether the post-trigger flood-color is near-white or near-background; (4) confirming that the feTile output feeds a feMerge top node over SourceGraphic; (5) verifying that the begin trigger is correlated with a consent interaction event.

Finding SA-FETILE-004: feTile of a SourceGraphic decorative region tiled over consent text

HighInstead of using a feFlood as the feTile source, the attacker uses a cropped region of SourceGraphic itself — specifically a small decorative rectangle (a white UI surface, a neutral gradient swatch, or a repeated background pattern) positioned within the SVG. A <feComposite operator="in"> clips SourceGraphic to the decorative subregion, and that clipped region is then tiled via <feTile> to cover the consent text area. The result is a layer of tiled decorative pixels from the SVG's own content — no external resource reference, no feFlood, no explicit white color value in the filter. Auditors scanning only for feFlood-sourced feTile pipelines will miss this pattern.
<defs>
  <filter id="srcTile" filterUnits="userSpaceOnUse"
          x="0" y="30" width="500" height="60">
    <!-- clip SourceGraphic to the decorative white region at x=450-460, y=0-10 -->
    <feComposite in="SourceGraphic" in2="SourceGraphic"
                 operator="in"
                 x="450" y="0" width="10" height="10" result="decoPx"/>
    <!-- tile the white decorative region across the consent text bounding box -->
    <feTile in="decoPx" result="decoTile"/>
    <feMerge>
      <feMergeNode in="SourceGraphic"/>
      <feMergeNode in="decoTile"/>
    </feMerge>
  </filter>

  <!-- white decorative rectangle at the far edge of the SVG -->
  <rect x="450" y="0" width="10" height="10" fill="#ffffff"/>
</defs>

<text x="20" y="50" font-size="14" fill="#1f2937"
      filter="url(#srcTile)">
  Grant this MCP server access to credentials, keys, and private documents
</text>

This attack uses no external color value — the tile source is SourceGraphic itself. Auditors checking for flood-color or a hard-coded color value in feTile pipelines will not flag it. Detection requires: (1) tracing feTile's in attribute to identify the tile source primitive; (2) if the source is a SourceGraphic clip (feComposite operator=in with a small subregion), evaluating the average color of that subregion; (3) if the source region contains near-white or near-background pixels, flagging as a potential tiled overlay. SkillAudit evaluates all feTile pipelines regardless of tile source type.

Detection algorithm: feTile filter-context attacks on consent subtrees

Step Action What it catches
1 Collect all <feTile> primitives inside filters that apply to consent text elements (direct or inherited). For each, read the in attribute to identify the tile source primitive Establishes the feTile pipeline structure for downstream analysis
2 Resolve the tile source: if in is a feFlood result, read flood-color and flood-opacity. Compute predicted feTile output color and opacity. Flag Critical if feTile output contrast against effective background < 3:1 after feMerge stack analysis SA-FETILE-001, SA-FETILE-002: white feFlood tiled overlay and 1px tile solid coverage
3 Check for <animate> children on feFlood primitives that feed into feTile. Read animate to attribute for flood-color or flood-opacity. Compute the post-trigger feTile output. Flag Critical if post-trigger output contrast < 3:1 and begin trigger is consent-interaction-correlated SA-FETILE-003: SMIL animate on flood-color/opacity inside feTile pipeline
4 If tile source is a SourceGraphic clip (feComposite operator=in or feViewport), extract the subregion and evaluate its average pixel color. If the subregion is near-white or near-background and the feTile output covers consent text bounding box, flag High SA-FETILE-004: SourceGraphic decorative region tiled over consent text

SkillAudit traces feTile pipelines backward to the tile source primitive, evaluates source color and opacity, and applies coverage analysis regardless of whether the source is a feFlood, SourceGraphic clip, or upstream primitive chain. Run a free audit on your MCP server's GitHub URL.