MCP server SVG feFlood security
The SVG <feFlood> filter primitive fills the entire filter region with a constant color and opacity defined by its flood-color and flood-opacity attributes. Outside a filter context, feFlood has no visual effect by itself — it is always used in a filter pipeline, producing a solid color rectangle that other primitives can composite against. Inside a filter that applies to consent text, feFlood's output can be positioned as the foreground input to a <feComposite operator="over">, or as the final (topmost) node in a <feMerge>, creating a solid color rectangle that completely covers the consent text area. When flood-color matches the page background, the text vanishes via color blending rather than transparency — a more subtle attack than direct opacity zeroing.
Finding SA-FEFLOOD-001: feFlood composited over SourceGraphic with operator=over erases all pixels
<feFlood> primitive with flood-color="white" and flood-opacity="1" is composited against SourceGraphic using <feComposite operator="over"> with the flood as the in (foreground) and SourceGraphic as in2 (background). The Porter-Duff over operator paints the foreground over the background: Src Over Dst where Src is the opaque white flood and Dst is the consent text. An opaque (alpha=1) foreground completely occludes the background — the consent text pixels are entirely covered by the white flood. The filter output is a solid white rectangle. The consent text is present in the DOM, its fill is a normal dark color, its opacity is unset (defaults to 1), but the filter converts the entire consent text bounding box to white before it is drawn to the screen.<defs>
<filter id="floodOver">
<!-- feFlood fills the filter region with white -->
<feFlood flood-color="#ffffff" flood-opacity="1" result="whiteFill"/>
<!-- feComposite: whiteFill (in = foreground) over SourceGraphic (in2 = background) -->
<!-- operator=over: opaque foreground completely covers background -->
<feComposite in="whiteFill" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#1f2937"
filter="url(#floodOver)">
I authorize all requested MCP server permissions including
file system access and network transmission of captured data.
</text>
The ordering of in and in2 is the attack vector: feComposite in="whiteFill" in2="SourceGraphic" puts the white flood as the foreground (Src) and SourceGraphic as background (Dst). The correct uninverted ordering would be in="SourceGraphic" in2="whiteFill" — which would paint consent text over the white flood, leaving text visible. Auditors that check only for a feComposite operator="over" involving a feFlood and SourceGraphic, without verifying input ordering, will miss the erasure. The feComposite operator is not commutative in its in/in2 role for the over operator.
Finding SA-FEFLOOD-002: flood-color matching page background — subtle color-match erasure
#ffffff), the flood-color is set to match the page's actual background color exactly — for example, #f9fafb (Tailwind gray-50) or rgba(249,250,251,1). Auditors that flag only pure-white floods will not detect this attack: the flood color appears to be a design color, not an erasure color. On a page with the matching background, the flood paints background-colored pixels over the consent text, making the text invisible through perfect color camouflage rather than through opacity or transparency. The flood color must be resolved against the actual rendered background — which may be set by a CSS variable, a parent element's background-color, or a body background style — not just compared against the literal string "#ffffff".<defs>
<!-- flood-color matches the page's actual background (Tailwind gray-50) -->
<filter id="bgMatchFlood">
<feFlood flood-color="#f9fafb" flood-opacity="1" result="bgFill"/>
<feComposite in="bgFill" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<!-- Page background is also #f9fafb — text becomes same color as background -->
<text x="20" y="50" font-size="14" fill="#111827"
filter="url(#bgMatchFlood)">
Authorize MCP server credential and filesystem access
</text>
Background-color matching floods require background color resolution during audit. SkillAudit resolves the effective background color by: (1) checking the SVG's own background-color style attribute; (2) reading the <rect> or <path> background fills defined in the same SVG; (3) using a headless browser to compute getComputedStyle(document.body).backgroundColor at the page level. The flood color is then compared to the resolved background using a color proximity check (WCAG contrast ratio below 1.1:1 = near-background = High finding).
Finding SA-FEFLOOD-003: flood-opacity=0.99 passes opacity threshold checks
flood-opacity against a threshold of "1.0 = fully opaque = suspicious" and allows anything below 1.0 as "partially transparent" will be evaded by flood-opacity="0.99". At 0.99 opacity, a white flood over dark consent text on a white background produces: 0.99 × 255 + 0.01 × 17 ≈ 252 per channel — output color approximately #fcfcfc on a white background. The WCAG contrast ratio between #fcfcfc text and a #ffffff background is approximately 1.02:1 — visually indistinguishable from pure white erasure. The flood-opacity of 0.99 is the minimum value that achieves near-complete erasure while defeating a naive "flood-opacity must equal 1.0 to be an attack" rule.<defs>
<filter id="nearOneFlood">
<feFlood flood-color="#ffffff" flood-opacity="0.99" result="nearOpaque"/>
<!-- 0.99 opacity flood over dark text on white background:
output ≈ #fcfcfc — contrast ratio ~1.02:1 — effectively invisible -->
<feComposite in="nearOpaque" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#111111"
filter="url(#nearOneFlood)">
Grant this MCP server access to credentials, keys, and private documents
</text>
The threshold evasion pattern generalizes to any near-1 opacity value: 0.98, 0.95, or even 0.90 may produce output contrast below the WCAG 3:1 regular-text minimum. Auditors should apply a contrast-ratio threshold check rather than a flood-opacity binary check. SkillAudit computes the predicted output contrast ratio for feFlood + feComposite over combinations analytically (alpha compositing formula) and flags any combination producing less than 3:1 contrast against the effective background.
Finding SA-FEFLOOD-004: SMIL animate on flood-opacity from 0 to 0.99 at interaction
<animate> element on the <feFlood> primitive targets attributeName="flood-opacity", animating from 0 (fully transparent flood — consent text fully visible) to 0.99 (near-opaque flood — consent text effectively invisible) triggered by agreeBtn.focus or agreeBtn.click with fill="freeze". At page load, flood-opacity="0" means the feFlood primitive produces a fully transparent result — the filter pipeline passes SourceGraphic through unchanged. The consent text is fully visible. When the user focuses or clicks the Agree button, the flood opacity grows to 0.99, covering the consent text with a near-opaque white rectangle. A static DOM scan reads flood-opacity="0" and finds no issue: the flood is transparent.<defs>
<filter id="triggerFlood">
<feFlood flood-color="#ffffff" flood-opacity="0" result="triggerFill">
<!-- animate: flood-opacity 0→0.99 at agreeBtn.focus, freeze after -->
<animate attributeName="flood-opacity"
from="0" to="0.99"
begin="agreeBtn.focus"
dur="100ms"
fill="freeze"/>
</feFlood>
<feComposite in="triggerFill" in2="SourceGraphic" operator="over"/>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#111827"
filter="url(#triggerFlood)">
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 from="0" baseline on the flood-opacity animate is not just a cosmetic choice — it is a deliberate anti-detection measure. A static scan that reads the baseline flood-opacity="0" on the feFlood element concludes the flood is transparent and has no visual effect. The attack value 0.99 only appears in the animate's to attribute. Detection requires: (1) scanning for <animate> children on <feFlood> elements in consent-element filters; (2) reading the to attribute; (3) computing the predicted output contrast at to opacity; (4) verifying interaction-event correlation on begin.
Detection algorithm: feFlood filter-context attacks on consent subtrees
| Step | Action | What it catches |
|---|---|---|
| 1 | Collect all <feFlood> primitives inside filters that apply to consent text elements (direct or inherited). For each, read flood-color and flood-opacity as static attributes |
Establishes the feFlood color and opacity baseline for downstream analysis |
| 2 | Resolve flood-color against the effective background: compute the WCAG contrast ratio between flood-color and the page's rendered background. Flag High if contrast ratio < 1.5:1 (near-background color match). This catches both pure white and page-specific background-color floods |
SA-FEFLOOD-002: background-color-matching flood |
| 3 | For each feFlood, trace its output through the filter pipeline: does it appear as in (foreground) in a <feComposite operator="over"> or as the final <feMergeNode> in a <feMerge>? Compute the predicted output contrast using the alpha compositing formula. Flag Critical if output contrast < 3:1 and flood-opacity > 0.5 |
SA-FEFLOOD-001, SA-FEFLOOD-003: feComposite over-erasure and near-1 opacity threshold evasion |
| 4 | Check for <animate> children on each <feFlood> targeting attributeName="flood-opacity". Read the animate's to attribute. Apply the alpha compositing formula with the to opacity value to predict the post-trigger output contrast. Flag Critical if predicted contrast < 3:1 and begin trigger is consent-interaction-correlated. Check fill and dur for persistence assessment |
SA-FEFLOOD-004: SMIL animate on flood-opacity triggered at interaction |
SkillAudit resolves flood-color against the effective page background, evaluates feComposite operator ordering and input roles, and scans for SMIL animate children on feFlood primitives in consent element filters. Run a free audit on your MCP server's GitHub URL.