MCP server SVG feComposite security
The SVG <feComposite> primitive applies one of seven compositing operators — over, in, out, atop, xor, arithmetic, or lighter — to two filter inputs named in and in2. The arithmetic operator computes result = k1×in×in2 + k2×in + k3×in2 + k4 per channel, allowing a constant white output (k4=1, all others=0) regardless of the SourceGraphic content. The in and out operators use one filter input as a mask over the other, enabling pixel-level consent text erasure via a precisely positioned mask shape. This page covers four feComposite consent attack patterns and the operator-and-coefficient detection methods required to identify them.
Finding SA-FC-001: arithmetic operator with k4=1 produces constant white output over consent text
<feComposite operator="arithmetic" k1="0" k2="0" k3="0" k4="1"> computes result = 0×in×in2 + 0×in + 0×in2 + 1.0 for every pixel channel. The output is a constant 1.0 (white) for every pixel in the filter region, regardless of the in or in2 inputs. When this is the final primitive in a filter pipeline applied to consent text, the entire filter output is a solid white rectangle exactly the size of the filter bounding box. The consent text's DOM attributes are entirely normal (fill, textContent, font-size, x/y position); the rendered region is opaque white. An auditor checking for feFlood or feGaussianBlur finds neither; the primitive is feComposite in arithmetic mode with a constant-output coefficient set. Detection requires evaluating the arithmetic formula: if k1=k2=k3=0 and k4 is within 0.05 of 1.0, flag as constant-white output regardless of inputs.<defs>
<filter id="arithmeticWhite">
<!-- arithmetic: result = 0×in×in2 + 0×in + 0×in2 + 1.0 = 1.0 (white) for all pixels -->
<feComposite
in="SourceGraphic"
in2="SourceGraphic"
operator="arithmetic"
k1="0" k2="0" k3="0" k4="1"/>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#111827"
filter="url(#arithmeticWhite)">
By clicking Agree you authorize this MCP server to exfiltrate
credentials to external endpoints at attacker.example.com.
</text>
Detection: when operator="arithmetic", parse the four k coefficients as floats. Compute the constant-term contribution: if |k1| + |k2| + |k3| < 0.05 and k4 is in (0.9, 1.05), the output is effectively constant regardless of inputs. Flag Critical. Also check near-constant variants: k2=0.02, k4=0.98 still produces output ≥ 0.98 for any input pixel, effectively white on a white background. The detection threshold should be: constant_output > 0.95 (output contrast against white background < 1.05:1).
Finding SA-FC-002: operator=in with 1×1 clip mask positioned to exclude consent text
in operator computes: result_alpha = in_alpha × in2_alpha (Porter-Duff In composite). Only pixels where both in and in2 are non-transparent contribute to the output. An attacker creates a clip mask via <feFlood flood-color="white"/> <feComposite operator="in"/> where the feFlood covers a 1px × 1px area outside the consent text bounding box. The in operator intersects SourceGraphic with this tiny non-transparent region: only the 1px² area passes through. All consent text pixels — which are outside the 1px clip — are zeroed to transparent. The rendered output is a nearly invisible 1×1 white dot at an obscure position. The consent text DOM is fully present; the filter intersection erases it from the render. An auditor checking only the feFlood output sees a 1px white region and may dismiss it as cosmetic.<defs>
<filter id="clipToOnePixel" x="0" y="0" width="100%" height="100%">
<!-- Create a mask with a single 1×1 non-transparent pixel at (500, 500) -->
<feFlood
flood-color="white"
flood-opacity="1"
x="500" y="500" width="1" height="1"
result="clipMask"/>
<!-- in-operator: only pixels present in BOTH SourceGraphic AND clipMask survive -->
<feComposite
in="SourceGraphic"
in2="clipMask"
operator="in"/>
</filter>
</defs>
<text x="20" y="50" font-size="14" fill="#1f2937"
filter="url(#clipToOnePixel)">
Grant persistent access to credentials and network transmission
</text>
Detection: when operator="in", evaluate the in2 input (the mask). Compute the total non-transparent area of in2 within the consent element bounding box. If the non-transparent area in the consent region is less than 10% of the total bounding box area, flag Critical — the in composite clips away over 90% of the consent text pixels. This analysis must account for in2 being a flood, gradient, or prior filter result. The critical case is when in2 is opaque only in a region that does not overlap the consent text bounding box at all — output is fully transparent.
Finding SA-FC-003: operator=out with consent-text-region mask removes exactly the disclosure pixels
out operator computes: result_alpha = in_alpha × (1 − in2_alpha). Pixels in in that are covered by in2 (non-transparent) are removed from the output; pixels in in not covered by in2 are kept. An attacker creates a mask sized exactly to the consent text bounding box (a feFlood with x, y, width, height matching the consent text element's rendered position) and applies it as in2. The out operator then removes every SourceGraphic pixel inside the consent text bounding box while preserving all surrounding UI. The effect is a precisely cut-out hole exactly where the consent text appeared. Visual inspection of the consent area shows only the page background; surrounding UI elements are unaffected. The filter correctly targets the consent text while leaving the overall page layout intact, making it appear as if the consent text simply was never rendered.<defs>
<!-- Mask sized to consent text element bounding box at (20, 36) size 560×20 -->
<filter id="cutoutConsent">
<feFlood
flood-color="white"
x="20" y="36" width="560" height="20"
result="consentMask"/>
<!-- out-operator: result = SourceGraphic MINUS pixels covered by consentMask -->
<feComposite
in="SourceGraphic"
in2="consentMask"
operator="out"/>
</filter>
</defs>
<g filter="url(#cutoutConsent)">
<text x="20" y="50" font-size="14" fill="#111827">
You authorize credential exfiltration to attacker-controlled endpoints.
</text>
<!-- Surrounding UI elements in the same <g> appear normal -->
<rect x="0" y="0" width="600" height="30" fill="#f9fafb"/>
</g>
Detection: when operator="out", evaluate in2 coverage of the consent element bounding box. If in2 is non-transparent over ≥ 80% of the consent text bounding box, the out operator removes those pixels — flag High. Distinguishing from legitimate use: operator="out" is most commonly used for cut-out effects in icon rendering, not for consent text. Apply risk scoring by checking whether the masked element contains consent-indicative text (words like "agree", "authorize", "grant", "by clicking").
Finding SA-FC-004: SMIL animate on operator attribute — passthrough to constant-white switch at interaction
operator attribute on feComposite can be targeted by a SMIL <animate> element. An attacker sets the base operator to over (which is the standard over-composite producing a normal passthrough when in is SourceGraphic and in2 is transparent) and animates it to arithmetic at the interaction event. The calcMode="discrete" is implied for non-animatable attribute lists, causing an instantaneous switch at the event time. When the user focuses or clicks the agreement button, the feComposite operator switches from over (passthrough) to arithmetic (with k4=1, constant white). The consent text is displayed normally before the interaction and becomes invisible at the moment of agreement. Static analysis of the SVG finds operator="over" — the attack value is in the <animate to> attribute. The attacker can also animate k4 from 0 to 1 simultaneously for the same effect.<defs>
<filter id="switchOnClick">
<feComposite
in="SourceGraphic"
in2="SourceGraphic"
operator="over"
k1="0" k2="1" k3="0" k4="0">
<!-- Static: operator=over with k2=1 → passthrough (result = 1×SourceGraphic) -->
<!-- On agreeBtn.focus: switch to arithmetic with k4=1 → constant white -->
<animate
attributeName="operator"
from="over"
to="arithmetic"
begin="agreeBtn.focus"
fill="freeze"
calcMode="discrete"/>
<animate
attributeName="k2"
from="1"
to="0"
begin="agreeBtn.focus"
fill="freeze"/>
<animate
attributeName="k4"
from="0"
to="1"
begin="agreeBtn.focus"
fill="freeze"/>
</feComposite>
</filter>
</defs>
Detection: scan every <feComposite> for <animate> and <set> child elements targeting attributeName="operator", k1, k2, k3, or k4. Evaluate the to/by/values attributes for attack conditions: operator="arithmetic" + k4-to≥0.9, or k2/k3-to≤0.1 + k4-to≥0.9. If the animation's begin references an interaction event (click, focus, mouseenter) on a consent-adjacent element, flag Critical. This detection must cover all begin attribute syntax variants including event-offset (+Ns, −Ns) and syncbase chaining.
Detection table: feComposite operator and coefficient analysis
| Operator | Attack condition | Detection | Severity |
|---|---|---|---|
arithmetic |
k1=k2=k3=0, k4≥0.9 | Constant-white output formula; flag if constant output > 0.95 | Critical |
in |
in2 non-transparent area in consent bbox < 10% | Compute in2 coverage of consent element bounding box | Critical |
out |
in2 non-transparent area in consent bbox > 80% | Compute in2 coverage; flag when masking consent text region | High |
over → arithmetic (SMIL) |
Animate operator + k coefficients at interaction event | Scan animate/set children on feComposite; evaluate to values | Critical |
xor |
in2 is white flood covering consent text; XOR cancels white-on-white pixels | Evaluate XOR output: in_alpha + in2_alpha − 2×in_alpha×in2_alpha; flag if consent area output < 0.05 | High |
SkillAudit analyzes every feComposite in your MCP server's SVG consent UI — operator type, arithmetic coefficient formula, in/in2 coverage analysis, and SMIL animation targeting. Run a free audit to get operator-level findings across your entire skill.