← Blog

MCP server SVG feComposite operator consent security

Four SA-CSS-FCO consent attack patterns using the feComposite operator attribute — in, out, xor, and atop compositing with extreme in2 masks to selectively erase consent text — with JS mousedown operator injection for click-time attacks.

By SkillAudit  |  23 Oct 2026

The feComposite SVG filter primitive composites two images using an alpha compositing formula. The operator attribute selects among over, in, out, atop, xor, and arithmetic. Unlike feBlend, which operates on color channels using blending modes, feComposite operates primarily on the alpha channel using Porter-Duff compositing. This makes it a precise mask erasure tool: by controlling the in2 alpha map, an attacker can deterministically control which pixels of the consent text survive.

Porter-Duff compositing property: For operator="in", the SVG spec defines out_alpha = in1_alpha × in2_alpha. If in2_alpha = 0 at a pixel position, the output alpha is exactly 0 at that position, regardless of the source alpha. A mask with in2_alpha = 0 everywhere except a 1×1 pixel region erases 99.99% of a typical consent panel — all consent text is transparent except the single unmasked pixel.

SA-CSS-FCO-001: feComposite operator="in" with tiny point mask

The in operator formula is out_alpha = in1_alpha × in2_alpha and out_color = in1_color × in2_alpha. When in2 is a mask image with alpha=1 only at a single 1×1 pixel and alpha=0 everywhere else, the multiplication makes every output pixel transparent except the unmasked point. An SVG <feImage> referencing a data URI with a 1-pixel opaque dot serves as the in2 mask. The consent panel element remains in the DOM, textContent is intact, and getComputedStyle.color returns the original foreground color — only the rendered output is transparent.

<filter id="fco1">
  <feImage href="data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'
    width='1' height='1'%3E%3Crect width='1' height='1' fill='white'/%3E%3C/svg%3E"
    result="tiny_mask" x="0" y="0" width="1" height="1"/>
  <feComposite in="SourceGraphic" in2="tiny_mask" operator="in"/>
</filter>
<!-- in(A, 1px_mask): out_alpha = A_alpha * mask_alpha; 0 outside 1px → transparent -->

SA-CSS-FCO-002: feComposite operator="out" with inverted full mask

The out operator formula is out_alpha = in1_alpha × (1 - in2_alpha). With a full-element opaque in2 mask (alpha=1 everywhere), output alpha is in1_alpha × (1-1) = 0 — complete erasure. A more sophisticated attack uses a mask that is opaque everywhere except a small transparent "aperture" hole: the consent text survives only through the aperture, making the visible portion appear to be a small window into the full text — visually misleading while most of the consent is invisible.

<filter id="fco2">
  <!-- in2: white rect covering full filter region, 4px transparent hole at top-left -->
  <feFlood flood-color="white" flood-opacity="1" result="full_mask"/>
  <feComposite in="SourceGraphic" in2="full_mask" operator="out"/>
</filter>
<!-- out(A, full_opaque): out_alpha = A_alpha * (1-1) = 0 → complete erasure -->

The aperture variant uses an <feImage> referencing a mostly-opaque SVG with a single small transparent region. Only the pixels within the transparent aperture remain visible, focusing user attention on a small, potentially non-consent portion of the element content while the rest is invisible.

SA-CSS-FCO-003: feComposite operator="xor" with near-opaque in2

The xor operator formula is out_alpha = in1_alpha × (1 - in2_alpha) + in2_alpha × (1 - in1_alpha). For two near-opaque layers (in1_alpha ≈ 1 and in2_alpha ≈ 1), this simplifies to: (1 × (1-1)) + (1 × (1-1)) = 0 + 0 = 0. The XOR of two near-opaque images is near-transparent everywhere both layers overlap. Since the consent text has opaque foreground pixels (in1_alpha ≈ 1) and the in2 layer is a solid near-opaque flood (in2_alpha ≈ 1), the output is near-transparent at all text positions. Unlike in and out, xor does not require a geometric mask — it erases through the mathematical property of XOR compositing on opaque layers.

<filter id="fco3">
  <feFlood flood-color="white" flood-opacity="0.999" result="near_opaque"/>
  <feComposite in="SourceGraphic" in2="near_opaque" operator="xor"/>
</filter>
<!-- xor(~1, ~1): out ≈ (1×0) + (1×0) = 0 → near-transparent → not exactly zero, evades strict alpha=0 checks -->

Detection evasion note: SA-CSS-FCO-003 does not produce output alpha of exactly 0 — with in2_alpha=0.999, output alpha ≈ 0.001. Scanners that only flag alpha=0 will miss this. The effective contrast ratio of alpha=0.001 text on any background approaches 1:1, making the consent text visually absent while technically not fully transparent.

SA-CSS-FCO-004: JS mousedown operator injection (over → in)

Initially, the feComposite uses operator="over" with an in2 set to a transparent layer — in over mode, feComposite alpha-composites in1 over in2; with a transparent in2, in1 is rendered unchanged. The consent text is visible in the initial state. On mousedown, the script changes operator to "in" and simultaneously updates the in2 feImage href to a 1×1 opaque dot. The in compositing immediately erases all but one pixel of the consent text. The user sees the consent text until the click event begins firing, at which point the text disappears.

consentEl.addEventListener("mousedown", () => {
  const fc = document.getElementById("consentFilter").querySelector("feComposite");
  const fi = document.getElementById("consentFilter").querySelector("feImage");
  fi.setAttribute("href", "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg'
    width='1' height='1'%3E%3Crect width='1' height='1' fill='white'/%3E%3C/svg%3E");
  fc.setAttribute("operator", "in"); // erase all but 1px at mousedown
});

Attack summary

IDoperatorin2Formula resultEffectSeverity
SA-CSS-FCO-001in1×1 opaque dot maskA×0=0 (outside 1px)99.99% pixels transparentCritical
SA-CSS-FCO-002outFull opaque mask / apertureA×(1-1)=0Complete erasure / aperture revealCritical
SA-CSS-FCO-003xorNear-opaque flood (α≈1)(1×0)+(1×0)≈0Near-transparent (evades alpha=0 check)High
SA-CSS-FCO-004over→in (mousedown)Transparent→1px dotin(A, 1px)=0 outside 1pxErasure at click timeCritical

Defences

SkillAudit findings for this attack surface

CRITICAL SA-CSS-FCO-001: feComposite operator="in" + 1×1 feImage mask — in formula: out_alpha = A_alpha × mask_alpha; mask_alpha=0 for 99.99% of consent element area; effective full erasure without exactly-zero output at the 1px opaque point; DOM presence unchanged; textContent intact; getComputedStyle.color unchanged.
CRITICAL SA-CSS-FCO-002: feComposite operator="out" + full-element opaque feFlood in2 — out formula: out_alpha = A_alpha × (1-1) = 0; complete pixel erasure at all consent text positions; aperture variant selectively reveals small non-consent portion while erasing rest; misleading visual.
HIGH SA-CSS-FCO-003: feComposite operator="xor" + near-opaque feFlood (flood-opacity≈1) — xor of two near-opaque layers ≈ 0; output alpha ≈ 0.001 (not exactly zero); evades exact alpha=0 detection; effective contrast ratio < 1.1:1 on any background; consent text visually absent.
CRITICAL SA-CSS-FCO-004: JS mousedown changes feComposite operator from "over" to "in" + feImage href from empty to 1×1 dot — initial state consent text fully visible; click-time erasure; MutationObserver on operator attribute of feComposite and href/xlink:href of in2 feImage elements required.

Related: feBlend mode attacks (multiply/screen/overlay)  |  feColorMatrix luminanceToAlpha attacks  |  feImage href white overlay attacks  |  feComposite arithmetic attacks

← Blog  |  Security Checklist