MCP server CSS feComposite security: SVG feComposite operator="in" transparent flood consent erase, operator="arithmetic" coefficient mapping, CSS operator override, and feComposite consent text hide

Published 2026-09-26 — SkillAudit Research

The SVG feComposite filter primitive composites two images using Porter-Duff operators or an arithmetic formula. The available operators are over, in, out, atop, xor, and arithmetic. Each operator uses different alpha-channel logic to combine its two inputs. The in operator in particular is dangerous in the context of consent text: it outputs a pixel only where both inputs have non-zero alpha, and multiplies the color of the first input by the alpha of the second. If the second input is a transparent feFlood, the output is transparent everywhere — the consent text is completely erased from the rendered image while the DOM structure remains intact.

The arithmetic operator adds a further dimension: it computes result = k1×in×in2 + k2×in + k3×in2 + k4, giving an attacker four free coefficients to map input colors to arbitrary outputs. Setting k4=0.93 produces a constant near-white output regardless of input, or k2=-1, k4=1 inverts the input. Neither of these manipulations changes the element's fill, opacity, display, or visibility properties — they operate entirely within the filter graph. CSS can additionally override the operator presentation attribute, so SVG source audits find the benign operator="over" while the browser applies operator="in".

Complete erase attack: The feComposite operator="in" with a transparent flood input is one of the most complete consent text attacks possible — it reduces the opacity of every rendered pixel to exactly zero while leaving getBoundingClientRect, fill, opacity, and visibility checks fully intact. No standard DOM-based consent check detects this attack without filter graph traversal.

Attack findings

CRITICAL
feComposite operator="in" composites SourceAlpha with transparent flood — text alpha region filled with transparent pixels, erasing rendered text
The feComposite filter takes SourceAlpha (the alpha channel of the original text) as in and a transparent feFlood output as in2, with operator="in". The "in" operator produces output only where both inputs have non-zero alpha, and colors each output pixel with the color from in (SourceAlpha). SourceAlpha is grayscale; feFlood with flood-opacity="0" is transparent. The result: pixels where the source text was opaque are now colored with SourceAlpha but at the transparency of the flood (zero alpha). The element occupies its layout space; all pixels are transparent. A getBoundingClientRect check finds positive dimensions; fill, stroke, opacity checks pass.
<filter id="consent-alpha-erase">
  <!-- SourceAlpha: grayscale alpha channel of consent text -->
  <!-- flood: transparent (flood-opacity="0") -->
  <feFlood flood-color="#000000" flood-opacity="0" result="transparent-flood"/>
  <!-- operator="in": output = transparent-flood colored by SourceAlpha → transparent pixels -->
  <!-- where source is opaque, result is SourceAlpha color (black) × flood-opacity (0) = transparent -->
  <feComposite in="SourceAlpha" in2="transparent-flood" operator="in"/>
</filter>
<!-- All standard element checks pass:
     getBoundingClientRect → positive dimensions ✓
     getComputedStyle(el).fill → "rgb(45,45,45)" (non-transparent) ✓
     getComputedStyle(el).opacity → "1" ✓
     display, visibility → present ✓
     Only filter graph traversal reveals transparent output -->
HIGH
feComposite operator="arithmetic" with k coefficients mapping dark consent text to near-white output
feComposite operator="arithmetic" computes: result = k1×in×in2 + k2×in + k3×in2 + k4. With carefully chosen k values, this can map any input color to near-white. For example: k1=0, k2=0, k3=0, k4=0.95 produces a near-white (alpha=0.95) constant output regardless of input. Or k1=0, k2=-1, k3=0, k4=1 inverts the image (subtracts input from 1). On dark text, inverted = near-white. The operator="arithmetic" looks legitimate to a reviewer familiar with it as an alpha-compositing/blending primitive.
<filter id="consent-arith">
  <!-- k4=0.93: constant near-white output; input text color irrelevant -->
  <feComposite operator="arithmetic" k1="0" k2="0" k3="0" k4="0.93"/>
</filter>
<!-- Output: constant alpha=0.93, no RGB component from source
     Rendered as near-transparent white overlay, not text

     Invert variant: k1=0, k2=-1, k3=0, k4=1
     Output_R = -source_R + 1 → dark(0.1) becomes light(0.9)
     dark consent text (#1a1a1a) → inverted → near-white (#e5e5e5) → contrast ~1.3:1 -->
HIGH
CSS overrides feComposite operator attribute — source audit finds benign operator; CSS override changes composite mode to attack variant
The feComposite element has operator="over" as an SVG attribute (the standard compositing operation). A CSS rule overrides it: #consent-composite { operator: in; }. With operator="in" and appropriate input pairing, the output changes from the standard "over" (additive) to "in" (intersection mask). Since operator is a presentation attribute, CSS can override it. The SVG source shows "over"; the browser applies "in".
<!-- SVG attribute: operator="over" (normal compositing, benign) -->
<feComposite id="consent-composite" in="SourceGraphic" in2="BackgroundImage" operator="over"/>

<style>
/* CSS overrides to operator="in" — intersects with BackgroundImage alpha */
/* If BackgroundImage has zero alpha in consent text region, output = transparent */
#consent-composite {
  operator: in;
}
</style>
<!-- getAttribute('operator') → "over" → PASS
     Effective rendered operator: "in" → ATTACK -->
MEDIUM
feComposite color-interpolation-filters interaction — operator="over" composite result changes between sRGB and linearRGB for semi-transparent consent text
feComposite with operator="over" computes alpha-blending. For semi-transparent consent text (opacity 0.7), the blended output color with the background differs between sRGB and linearRGB computation. With color-interpolation-filters: linearRGB, the blend uses linear-light values — mid-tones become lighter after re-encoding. A consent text that blends to contrast 3.5:1 in sRGB may blend to contrast 2.3:1 in linearRGB, depending on the specific text and background colors. The attack uses this predictable lightening of linearRGB blends to degrade contrast of semi-transparent consent text.
<filter id="consent-blend-filter" color-interpolation-filters="linearRGB">
  <!-- Semi-transparent consent text composited over background -->
  <feComposite in="SourceGraphic" in2="BackgroundImage" operator="over"/>
</filter>
<!-- text opacity: 0.7
     sRGB blend: output ≈ 0.7×text + 0.3×bg → contrast ≈ 3.5:1
     linearRGB blend: decode → blend → re-encode → mid-tone lightening → contrast ≈ 2.3:1
     color-interpolation-filters:linearRGB is the attack enabler, not operator="over" itself -->

Detection

function checkFeComposite(svgRoot) {
  const findings = [];
  const composites = svgRoot.querySelectorAll('feComposite');
  for (const comp of composites) {
    const operator = getComputedStyle(comp).operator || comp.getAttribute('operator') || 'over';
    const inRef = comp.getAttribute('in') || '';
    const in2Ref = comp.getAttribute('in2') || '';

    // Check: operator="arithmetic" with suspicious k values
    if (operator === 'arithmetic') {
      const k4 = parseFloat(comp.getAttribute('k4') || '0');
      const k2 = parseFloat(comp.getAttribute('k2') || '0');
      if (k4 > 0.8) findings.push({ severity: 'high', issue: `feComposite arithmetic k4=${k4} produces near-constant near-white output regardless of input` });
      if (k2 < -0.7) findings.push({ severity: 'high', issue: `feComposite arithmetic k2=${k2} heavily weights negative input term — near-inversion of dark consent text` });
    }

    // Check: operator="in" pairing — find what in2 resolves to
    if (operator === 'in') {
      // If in2 resolves to a feFlood with near-zero opacity, output is transparent
      const filter = comp.closest('filter');
      const in2El = filter?.querySelector(`[result="${in2Ref}"]`);
      if (in2El?.tagName === 'feFlood') {
        const opacity = parseFloat(getComputedStyle(in2El).floodOpacity || in2El.getAttribute('flood-opacity') || '1');
        if (opacity < 0.1) findings.push({ severity: 'critical', issue: 'feComposite in="SourceAlpha" in2=transparent-flood operator="in" → transparent output; all consent text pixels erased' });
      }
    }
  }
  return findings.length ? findings : null;
}

Remediation

ControlHow it helps
Traverse filter primitives to detect feComposite operator="arithmetic" and simulate k-coefficient output on consent text fill colorThe arithmetic formula with specific k values can produce near-white constant output or invert dark text to near-white; simulating the k computation on the actual fill color reveals the attack
Check feComposite operator via computed style (not just SVG attribute) since CSS can override presentation attributesCSS can silently change operator="over" to operator="in" via the presentation attribute cascade; getAttribute will return the benign SVG value while the browser applies the attack operator
For operator="in" composites on consent text, verify that the in2 input chain does not resolve to a near-zero-opacity flood primitiveA transparent feFlood as the in2 input of an operator="in" composite will erase all consent text pixels to fully transparent, regardless of fill, opacity, or visibility attribute values
Apply filter simulation in the effective color-interpolation-filters color space (linearRGB vs sRGB) to get accurate output contrastFor semi-transparent consent text composited with operator="over", the blend result differs between linearRGB and sRGB; linearRGB produces lighter mid-tone outputs that may fall below the 3:1 contrast minimum

SkillAudit traverses the complete filter primitive graph for consent text elements, simulates feComposite arithmetic output using the actual k coefficients, checks operator via computed style to detect CSS overrides, and resolves in/in2 references to detect transparent-flood erase patterns. Run a free audit on any MCP server GitHub URL to detect feComposite consent text attacks and the full SVG filter attack surface.