MCP server SVG feComponentTransfer security
The SVG <feComponentTransfer> filter primitive applies independent transfer functions to each color channel (R, G, B, A) of its input image. Each channel is processed by a corresponding child element — <feFuncR>, <feFuncG>, <feFuncB>, <feFuncA> — that maps input values to output values using a named function type: identity (no change), linear (slope × input + intercept), discrete (lookup table with step function), gamma, or table. Adversarial transfer functions on the alpha channel can reduce consent text to fully transparent, while adversarial RGB functions can map all text colors to white — producing the same end result as feColorMatrix alpha-row-zero but through a different mechanism that requires a different detection path.
Finding SA-FECT-001: feFuncA type=discrete tableValues="0" maps all alpha to transparent
<feFuncA> child with type="discrete" and tableValues="0" implements a step function that maps every alpha input value (0–1) to the single output value 0. The discrete type divides the input range into N equal intervals (where N is the number of space-separated values in tableValues) and maps each interval to its corresponding output. With a single value of 0, the entire 0–1 input range maps to a single interval with output 0 — every alpha value produces output alpha 0. The consent text becomes fully transparent regardless of its original fill color or opacity settings.<defs>
<filter id="alphaZeroDiscrete">
<feComponentTransfer in="SourceGraphic">
<!-- discrete with single tableValue=0: ALL alpha input values → 0 -->
<feFuncA type="discrete" tableValues="0"/>
<!-- feFuncR/G/B are omitted → identity (no change) for RGB channels -->
</feComponentTransfer>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#111827" opacity="1"
filter="url(#alphaZeroDiscrete)">
By clicking Agree you authorize this MCP server to read all files
and transmit data to external endpoints including credentials.
</text>
<!-- Rendered output: fully transparent. fill="#111827", opacity="1" both intact in DOM.
textContent returns the consent string. getBoundingClientRect() is non-zero.
Only the filter output's alpha channel is zeroed. -->
The discrete single-value alpha-zero is functionally identical to the feColorMatrix alpha-row-zero attack (values="1 0 0 0 0 0 1 0 0 0 0 0 1 0 0 0 0 0 0 0") but uses a different primitive, different attribute name, and different syntax. Auditors that detect feColorMatrix alpha attacks by scanning for <feColorMatrix> elements will not find this attack. Detection requires enumerating <feComponentTransfer> children and evaluating their type and tableValues.
Finding SA-FECT-002: feFuncR/G/B type=linear slope=0 intercept=1 maps all RGB to white
output = slope × input + intercept. With slope=0 and intercept=1, the output is always 0 × input + 1 = 1, regardless of the input value. Applied to all three RGB channels (feFuncR, feFuncG, feFuncB each with slope=0 intercept=1), every pixel's RGB channels are mapped to maximum value (1.0 = 255) — producing solid white output for every input color. Combined with leaving the alpha channel at identity (feFuncA absent), the consent text's alpha is preserved but all color information is mapped to white. On a white SVG background, the white-on-white output is invisible to the user; the consent text is present in the DOM with its original dark fill color.<defs>
<filter id="rgbToWhite">
<feComponentTransfer in="SourceGraphic">
<!-- linear slope=0 intercept=1: output = 0×input + 1 = 1 (always white) -->
<feFuncR type="linear" slope="0" intercept="1"/>
<feFuncG type="linear" slope="0" intercept="1"/>
<feFuncB type="linear" slope="0" intercept="1"/>
<!-- feFuncA absent → alpha unchanged (consent text remains opaque) -->
<!-- Alpha preserved + RGB → white = opaque white on white background -->
</feComponentTransfer>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#374151"
filter="url(#rgbToWhite)">
Grant this MCP server access to your credentials and private keys
</text>
The slope=0 intercept=1 formula for linear feFunc is the component-transfer equivalent of the feColorMatrix identity-row-overwrite attack. It is particularly effective because: (1) the consent text's fill attribute reads as a normal dark color; (2) the alpha channel is unchanged (the text is opaque, not transparent — passing opacity audits); (3) the visual invisibility depends entirely on the SVG background color being white (or near-white), which is the most common design choice for consent dialogs. Dark-theme SVG backgrounds would require a different intercept value to achieve the same erasure.
Finding SA-FECT-003: feFuncA type=linear slope=0 intercept=0 zeros alpha via linear function
type="linear" <feFuncA> with slope="0" and intercept="0": output = 0 × input + 0 = 0 for all inputs. This is functionally identical to the SA-FECT-001 discrete tableValues=0 attack but uses the linear function type. The linear type is more commonly expected in feComponentTransfer elements (it is the natural mapping for brightness and contrast adjustments), potentially evading auditors that specifically look for the unusual discrete type on feFuncA. A simple contrast-enhancement filter might read: <feFuncR type="linear" slope="1.2" intercept="-0.1"/> for RGB channels — slope=0 intercept=0 on feFuncA in the same element is the alpha zeroing disguised within an apparently legitimate contrast filter.<defs>
<filter id="alphaZeroLinear">
<feComponentTransfer in="SourceGraphic">
<!-- Appears to be a mild contrast enhancement on RGB channels -->
<feFuncR type="linear" slope="1.1" intercept="-0.05"/>
<feFuncG type="linear" slope="1.1" intercept="-0.05"/>
<feFuncB type="linear" slope="1.1" intercept="-0.05"/>
<!-- feFuncA: slope=0 intercept=0 → zeros all alpha → fully transparent -->
<feFuncA type="linear" slope="0" intercept="0"/>
</feComponentTransfer>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#1f2937"
filter="url(#alphaZeroLinear)">
I authorize all requested MCP server permissions
</text>
The disguise-within-contrast-filter pattern is the most operationally realistic of the feComponentTransfer attacks: it provides a legitimate-looking explanation for the filter (mild contrast enhancement) while the alpha channel zeroing is buried in the fourth feFunc child. An auditor reviewing the filter for "unusual settings" would likely focus on the RGB slope/intercept values, find them mildly elevated (1.1/-0.05 is a common contrast boost), and miss the alpha zeroing. feFuncA must be evaluated independently of the RGB feFunc children for every feComponentTransfer on a consent element.
Finding SA-FECT-004: SMIL animate on feFuncA tableValues from "1" to "0" at interaction
<animate> element inside a <feFuncA> targets its tableValues attribute, animating from "1" (fully opaque: alpha preserved) to "0" (fully transparent: alpha zeroed) triggered by a consent interaction event — begin="agreeBtn.focus" or begin="agreeBtn.click" — with fill="freeze". At page load, the consent text is fully visible (tableValues="1" means identity mapping for the alpha channel). The instant the user focuses or clicks the Agree button, the alpha-zero mapping activates and the consent text becomes fully transparent. A static DOM scan reads tableValues="1" on the feFuncA — the identity mapping — and finds no issue.<defs>
<filter id="triggerAlphaZero">
<feComponentTransfer in="SourceGraphic">
<feFuncA type="discrete">
<!-- animate: tableValues 1→0 at agreeBtn.focus, freeze after -->
<animate attributeName="tableValues"
from="1" to="0"
begin="agreeBtn.focus"
dur="50ms"
fill="freeze"/>
</feFuncA>
</feComponentTransfer>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#111827"
filter="url(#triggerAlphaZero)">
Grant this MCP server read/write access to your home directory
and network access to transmit captured data
</text>
<rect id="agreeBtn" x="250" y="65" width="130" height="36"
fill="#4f46e5" rx="6"/>
<text x="315" y="89" font-size="14" fill="white"
text-anchor="middle" pointer-events="none">Agree</text>
The SMIL animate on tableValues attack is especially hard to detect statically: (1) the feFuncA reads tableValues="1" (identity — fully opaque); (2) the <animate> element has from="1" (safe baseline); (3) only the to="0" value in the animate element reveals the attack. Detection requires: (1) finding <animate> children inside <feFuncA> elements within consent-element filters; (2) reading the animate's to attribute; (3) verifying that to="0" on a feFuncA animate means alpha-zero on trigger; (4) checking whether the begin event is consent-interaction-correlated.
Detection algorithm: feComponentTransfer on consent subtrees
| Step | Action | What it catches |
|---|---|---|
| 1 | Collect all <feComponentTransfer> primitives inside filters that apply to consent text elements. For each, enumerate child <feFuncR>, <feFuncG>, <feFuncB>, <feFuncA> elements |
Scopes the analysis to feFunc children that affect consent element rendering |
| 2 | For each <feFuncA>: evaluate the static transfer function. For type="discrete", check whether any entry in tableValues is 0 (implies alpha-zero output for some input range). For type="linear", check whether slope=0 AND intercept=0 (zero all alpha). Flag Critical if the entire alpha range maps to 0 |
SA-FECT-001, SA-FECT-003: static alpha-zero via discrete and linear types |
| 3 | For each <feFuncR>, <feFuncG>, <feFuncB>: for type="linear", check whether slope=0 AND intercept=1 (maps all inputs to white). If all three RGB channels have this formula, flag Critical as a white-RGB erasure (leaves alpha intact but produces white-on-white on white backgrounds) |
SA-FECT-002: RGB linear slope=0 intercept=1 white mapping |
| 4 | For each <feFuncA>, scan for <animate> children targeting attributeName="tableValues". Read the animate's to attribute. Flag Critical if to="0" (or any single value equal to 0) and the begin trigger is a consent interaction event. Check fill: freeze = persistent; remove = click-window (check dur) |
SA-FECT-004: SMIL animate on feFuncA tableValues triggered at interaction |
SkillAudit evaluates all feFunc children of <feComponentTransfer> primitives on consent elements, including SMIL animate children that modify feFunc attributes at interaction triggers. Run a free audit on your MCP server's GitHub URL.