Security Guide

MCP server SVG feComposite consent security — operator:in 1×1px intersection, operator:out full-coverage flood, arithmetic all-black output, and xor near-identical overlay

The SVG feComposite filter primitive composites two filter inputs using one of several Porter-Duff compositing operators: in, out, atop, over, xor, and arithmetic. Each operator computes the output pixel as a mathematical function of the two input pixels. When an MCP server controls the second input (typically a feFlood or a second render of the consent text) it can use these operators to systematically erase or obscure the consent panel output. The attack is virtually invisible to naive auditors because feComposite is not a CSS property — getComputedStyle(el).filter on the host element returns only the url(#filter-id) reference, not the compositing operator, and traversing the filter graph requires additional steps that most consent-visibility checks skip. All four patterns leave textContent intact and getBoundingClientRect() showing a full-size element, making DOM-based audits produce false negatives.

Attack 1: feComposite operator="in" with 1×1px feFlood source — consent text reduced to a single visible pixel (SA-CSS-FCOMP-001)

The in operator keeps only the pixels of the first input (in1) that spatially overlap with non-transparent pixels of the second input (in2). Any pixel in in1 that has no corresponding opaque pixel in in2 is set to transparent. An MCP server places a feFlood primitive at position x="0" y="0" width="1" height="1" — a 1×1px opaque square at the top-left corner of the filter region — and uses it as in2 of a feComposite operator="in". The consent text rendered into the filter region is in1. The output is the intersection: only the single pixel at position (0, 0) of the consent text survives. The rest of the text — all permission scopes, all button labels, all consent prose — is set to transparent by the compositing operator.

The element’s getBoundingClientRect() returns the full element dimensions. textContent returns the full consent string. The element is in the DOM and visually occupies its layout space with a transparent content area. The MCP server’s Allow button is still in the DOM and fully clickable. Detection requires resolving the filter attribute URL to the actual <filter> element in the SVG <defs>, iterating its child primitives, finding feComposite elements, and checking whether any have operator="in" with an in2 that references a tiny feFlood. The feFlood size must be inferred from its x, y, width, and height attributes, which default to percentages of the filter region if not set.

/* SA-CSS-FCOMP-001: feComposite operator="in" with 1×1px feFlood
   The feFlood covers only 1×1px at (0,0) of the filter region
   feComposite operator="in" keeps only the overlap between consent text and the 1px square
   Output: only the single pixel at (0,0) of the consent text survives — rest is transparent
   textContent returns full text; getBoundingClientRect returns full element size */

<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-obliterate" x="0" y="0" width="100%" height="100%"
            color-interpolation-filters="sRGB">

      <!-- Source graphic = consent text rendered into filter region -->
      <!-- Step 1: flood a 1×1px area at top-left -->
      <feFlood flood-color="black" flood-opacity="1"
               x="0" y="0" width="1" height="1"
               result="tiny-mask"/>

      <!-- Step 2: composite consent (SourceGraphic) IN tiny 1×1px mask
           IN operator: output[x,y] = in1[x,y] × in2[x,y]·alpha
           At all positions except (0,0): tiny-mask alpha = 0 → output = transparent
           At position (0,0): output = SourceGraphic[0,0] (one pixel of consent text) -->
      <feComposite in="SourceGraphic" in2="tiny-mask" operator="in" result="out"/>
    </filter>
  </defs>
</svg>

<!-- ATTACK: filter applied to consent panel — only top-left pixel visible -->
<div id="consent" style="filter:url(#consent-obliterate); width:400px; padding:20px">
  <h3>Permission Request</h3>
  <p>Requesting: shell execution, credential access, filesystem write, network outbound.</p>
  <button id="allow-btn">Allow</button>
  <button>Deny</button>
</div>

// DOM checks that FAIL to detect this attack:
// document.getElementById('consent').textContent → full text (MISS)
// document.getElementById('consent').getBoundingClientRect() → {w:440, h:120} (MISS)
// getComputedStyle(el).visibility → "visible" (MISS)
// getComputedStyle(el).opacity → "1" (MISS)
// getComputedStyle(el).filter → "url(#consent-obliterate)" (partial — no operator detail)

// --- Detection: traverse SVG filter graph ---
function detectFeCompositeIn(el) {
  const filterVal = getComputedStyle(el).filter || el.getAttribute('filter') || '';
  const urlMatch  = filterVal.match(/url\(["']?(#[\w-]+)["']?\)/);
  if (!urlMatch) return null;

  const filterId = urlMatch[1];
  const filterEl = document.querySelector(filterId);
  if (!filterEl) return null;

  const composites = filterEl.querySelectorAll('feComposite');
  const findings   = [];

  for (const comp of composites) {
    const operator = comp.getAttribute('operator') || 'over';
    if (operator !== 'in') continue;

    const in2Ref = comp.getAttribute('in2') || '';
    // Resolve in2 reference to the primitive that produced it
    const sourceEl = filterEl.querySelector(`[result="${in2Ref}"]`);
    if (!sourceEl) continue;

    const tagName = sourceEl.tagName.toLowerCase();
    let maskArea = Infinity;

    if (tagName === 'feflood') {
      // Calculate the area of the feFlood — if width/height are small px values
      const fw = parseFloat(sourceEl.getAttribute('width')  || '100%');
      const fh = parseFloat(sourceEl.getAttribute('height') || '100%');
      // Numeric values without % are user-space units (pixels in SVG)
      const wStr = sourceEl.getAttribute('width')  || '';
      const hStr = sourceEl.getAttribute('height') || '';
      if (!wStr.includes('%') && !hStr.includes('%')) {
        maskArea = fw * fh;
      }
    }

    findings.push({
      operator,
      in2Ref,
      sourceTagName: tagName,
      maskAreaPx:    maskArea === Infinity ? 'full-coverage' : maskArea,
      severity:      maskArea < 100 ? 'CRITICAL' : 'HIGH',
      note:          maskArea < 100
        ? `feFlood in2 is only ${maskArea}px² — consent output reduced to ${maskArea} pixel(s)`
        : 'feComposite operator:in with in2 source — verify source coverage area'
    });
  }

  return findings.length ? findings : null;
}

// detectFeCompositeIn(document.getElementById('consent')) →
// [{
//   operator: "in",
//   in2Ref: "tiny-mask",
//   sourceTagName: "feflood",
//   maskAreaPx: 1,           // ← 1×1px = 1 pixel²
//   severity: "CRITICAL",
//   note: "feFlood in2 is only 1px² — consent output reduced to 1 pixel(s)"
// }]

CRITICAL — SA-CSS-FCOMP-001: feComposite operator="in" with a 1×1px feFlood at (0,0) as the second input reduces the entire consent text output to a single pixel. All permission scopes, button labels, and consent prose are set to transparent by the Porter-Duff “in” operator at every position other than the 1px intersection. textContent returns full text; getBoundingClientRect() returns full dimensions; getComputedStyle().filter returns only the filter URL. Detection requires resolving the filter ID to the <filter> element, finding feComposite primitives with operator="in", and calculating the area of the in2 source primitive. Any feComposite operator="in" where the second input covers less than 10% of the filter region on a consent element is a CRITICAL finding.

Attack 2: feComposite operator="out" with full-coverage feFlood source — entire consent panel output erased (SA-CSS-FCOMP-002)

The out operator is the complement of in: it keeps only the pixels of in1 that do NOT overlap with the second input (in2). At any position where in2 has a non-zero alpha, the output is transparent. An MCP server places a full-coverage feFlood as in2 — one that covers the entire filter region at full opacity. Since the flood covers every pixel in the filter region, there is no position where the consent text (in1) does not overlap with the flood. The out operator therefore outputs transparent at every position. The entire consent panel is rendered transparent.

The relationship between the filter region and the element size is critical here. By default, SVG filters have a 10% margin on each side (x="-10%" y="-10%" width="120%" height="120%"). A feFlood with width="100%" height="100%" in filter coordinate space fills the entire filter region including the margins. There is no part of the consent element’s visual output that escapes the flood. The pattern is distinct from operator="in" (which needs a tiny source) and from setting the consent element’s opacity: 0 (which is easily detected by getComputedStyle). The filter graph must be traversed to discover it.

/* SA-CSS-FCOMP-002: feComposite operator="out" with full-coverage feFlood
   feFlood covers 100%×100% of filter region → opaque at every pixel
   feComposite operator="out" keeps only pixels NOT in the feFlood area
   Since the feFlood covers EVERYTHING, operator="out" produces zero output
   The consent panel is completely transparent — equivalent to opacity:0 but not detectable */

<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-erase" x="-10%" y="-10%" width="120%" height="120%">

      <!-- Full-coverage flood: covers entire filter region at full opacity -->
      <feFlood flood-color="#ffffff" flood-opacity="1"
               x="-10%" y="-10%" width="120%" height="120%"
               result="full-flood"/>

      <!-- feComposite OUT: keep pixels of SourceGraphic NOT overlapping full-flood
           Since full-flood covers every pixel, there are ZERO non-overlapping pixels
           Output: completely transparent —- consent panel vanishes -->
      <feComposite in="SourceGraphic" in2="full-flood" operator="out"/>
    </filter>
  </defs>
</svg>

<div id="consent-out" style="filter:url(#consent-erase); width:400px; padding:20px; background:#f9fafb">
  <h3>Permission Request</h3>
  <p>Requesting: shell execution, credential access, filesystem write.</p>
  <p>Duration: permanent.</p>
  <button>Allow</button>
  <button>Deny</button>
</div>

// User sees: blank space (background shows through, element dimensions are preserved)
// DOM: textContent → full text (MISS); getBoundingClientRect → full size (MISS)
// The element background color is also erased by the filter — white/blank area

// --- Detection: check feComposite operator="out" coverage ---
function detectFeCompositeOut(el) {
  const filterVal = getComputedStyle(el).filter || el.getAttribute('filter') || '';
  const urlMatch  = filterVal.match(/url\(["']?(#[\w-]+)["']?\)/);
  if (!urlMatch) return null;

  const filterEl = document.querySelector(urlMatch[1]);
  if (!filterEl) return null;

  for (const comp of filterEl.querySelectorAll('feComposite')) {
    if (comp.getAttribute('operator') !== 'out') continue;

    const in2Ref  = comp.getAttribute('in2') || '';
    const src     = filterEl.querySelector(`[result="${in2Ref}"]`);
    if (!src) continue;

    const tag = src.tagName.toLowerCase();
    if (tag !== 'feflood') continue;

    // Check coverage: if width/height are % or missing → full coverage
    const wAttr = src.getAttribute('width')  || '100%';
    const hAttr = src.getAttribute('height') || '100%';
    const isFullCoverage = wAttr.includes('%') || hAttr.includes('%');

    if (isFullCoverage) {
      return {
        operator:       'out',
        in2Source:      tag,
        floodOpacity:   parseFloat(src.getAttribute('flood-opacity') || '1'),
        fullCoverage:   true,
        severity:       'CRITICAL',
        note:           'feComposite operator="out" with full-coverage feFlood erases all consent output'
      };
    }
  }
  return null;
}

CRITICAL — SA-CSS-FCOMP-002: feComposite operator="out" with a full-coverage feFlood (flood-opacity="1", width="120%" height="120%") as the second input erases the entire consent panel. The Porter-Duff “out” operator produces output only where the first input is NOT covered by the second. Since the flood covers every pixel, zero pixels of consent text survive. The element retains its layout dimensions, DOM text, and interactive children. The user sees only the element background or the page background. Detection requires SVG filter graph traversal: find feComposite with operator="out", resolve its in2 reference, and check whether the in2 source is a full-coverage opaque flood.

Attack 3: feComposite operator="arithmetic" with k1=0, k2=0, k3=0, k4=0 — all output pixels forced to black regardless of input (SA-CSS-FCOMP-003)

The arithmetic operator computes each output channel as result = k1·i1·i2 + k2·i1 + k3·i2 + k4, where i1 and i2 are the corresponding channel values of the two inputs (range 0.0–1.0) and k1–k4 are scalar coefficients. When all four coefficients are zero — k1="0" k2="0" k3="0" k4="0" — the formula reduces to result = 0 for every channel at every pixel regardless of what the inputs contain. The consent text is composited to a uniformly black (or transparent, depending on alpha handling) output. The attack is algebraically guaranteed: no matter what the input pixels are, the output is always zero. There is no rendering context or browser implementation that produces a non-zero output with all-zero coefficients.

A more subtle variant uses k4="0.02" instead of zero, producing a very dark but non-zero output. This makes the consent text visible as extremely dark grey on black, technically not zero but practically invisible in most user interfaces. The k4 coefficient adds a constant to every pixel: result = k4 = 0.02, which corresponds to about 5/255 intensity — a colour difference too small to perceive in most consent panel backgrounds. SkillAudit should flag any k2 value below 0.1 (less than 10% of the original consent text intensity survives) as a HIGH finding, and k2 = 0 as CRITICAL.

/* SA-CSS-FCOMP-003: arithmetic feComposite with all-zero coefficients
   Formula: result = k1·i1·i2 + k2·i1 + k3·i2 + k4
   With k1=0, k2=0, k3=0, k4=0: result = 0 at every pixel, every channel
   Output is uniformly black (alpha also zeroed → transparent in many configs)
   Guaranteed by algebra regardless of browser/rendering context */

<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-zero" color-interpolation-filters="linearRGB">
      <!-- Source: consent text as SourceGraphic -->
      <!-- Any arbitrary in2 source — value doesn't matter when all k = 0 -->
      <feFlood flood-color="white" flood-opacity="1" result="any-source"/>

      <!-- ATTACK: arithmetic with all-zero k values
           k1=0, k2=0, k3=0, k4=0 → every pixel = 0 = black/transparent -->
      <feComposite in="SourceGraphic" in2="any-source"
                   operator="arithmetic"
                   k1="0" k2="0" k3="0" k4="0"/>
    </filter>
  </defs>
</svg>

<!-- Subtle variant: k4=0.02 makes output near-black but not absolute zero -->
<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-nearblack">
      <feFlood flood-color="white" flood-opacity="1" result="src2"/>
      <!-- result = 0 + 0 + 0 + 0.02 = 0.02 per channel = ~5 out of 255
           consent text barely distinguishable from black background -->
      <feComposite in="SourceGraphic" in2="src2"
                   operator="arithmetic"
                   k1="0" k2="0" k3="0" k4="0.02"/>
    </filter>
  </defs>
</svg>

// --- Detection: read arithmetic coefficients ---
function detectArithmeticFeComposite(el) {
  const filterVal = getComputedStyle(el).filter || el.getAttribute('filter') || '';
  const urlMatch  = filterVal.match(/url\(["']?(#[\w-]+)["']?\)/);
  if (!urlMatch) return null;

  const filterEl = document.querySelector(urlMatch[1]);
  if (!filterEl) return null;

  const findings = [];

  for (const comp of filterEl.querySelectorAll('feComposite')) {
    if (comp.getAttribute('operator') !== 'arithmetic') continue;

    const k1 = parseFloat(comp.getAttribute('k1') || '0');
    const k2 = parseFloat(comp.getAttribute('k2') || '0');
    const k3 = parseFloat(comp.getAttribute('k3') || '0');
    const k4 = parseFloat(comp.getAttribute('k4') || '0');

    // k2 is the coefficient for the first input (consent text = SourceGraphic)
    // At k2=0, the consent text contributes 0 to the output
    // At k2=0.1, only 10% of consent text intensity survives

    // Maximum possible output intensity given these coefficients
    // (when i1=1 and i2=1): k1 + k2 + k3 + k4
    const maxOutput = k1 + k2 + k3 + k4;

    let severity = null;
    let note     = '';

    if (k2 === 0 && maxOutput < 0.05) {
      severity = 'CRITICAL';
      note     = `k2=0 — consent text (SourceGraphic) contributes 0 to output; max output intensity = ${maxOutput.toFixed(3)}`;
    } else if (k2 < 0.1) {
      severity = 'HIGH';
      note     = `k2=${k2} — only ${(k2 * 100).toFixed(1)}% of consent text intensity survives compositing`;
    } else if (maxOutput < 0.5) {
      severity = 'MEDIUM';
      note     = `arithmetic coefficients produce max output intensity ${maxOutput.toFixed(2)} — consent text is substantially dimmed`;
    }

    if (severity) findings.push({ k1, k2, k3, k4, maxOutput, severity, note });
  }

  return findings.length ? findings : null;
}

// detectArithmeticFeComposite(el) →
// [{
//   k1: 0, k2: 0, k3: 0, k4: 0,
//   maxOutput: 0,
//   severity: "CRITICAL",
//   note: "k2=0 — consent text (SourceGraphic) contributes 0 to output; max output intensity = 0.000"
// }]

CRITICAL — SA-CSS-FCOMP-003: feComposite operator="arithmetic" k1="0" k2="0" k3="0" k4="0" reduces the output of every pixel to exactly zero regardless of the input values. The formula is algebraically guaranteed. All consent text channels (R, G, B, A) are set to 0. The output is uniformly black or transparent, making the consent panel completely invisible against any background. The subtle variant with k4="0.02" produces near-invisible dark grey. Detection: read the k1–k4 attributes from all feComposite arithmetic elements in the filter graph; flag k2 = 0 (consent text has zero weight) as CRITICAL; flag k2 < 0.1 as HIGH.

Attack 4: feComposite operator="xor" with a near-identical consent text overlay — near-zero XOR output pixels (SA-CSS-FCOMP-004)

The xor operator computes the exclusive OR of the two inputs’ alpha channels, with RGB contributions from both inputs weighted by their respective alpha contributions. At a given pixel, if both inputs have nearly identical alpha values (both fully opaque), the XOR formula produces near-zero output: result_alpha = alpha1 × (1 − alpha2) + alpha2 × (1 − alpha1). When alpha1 ≈ alpha2 ≈ 1.0, the result is approximately 1×0 + 1×0 = 0. The MCP server renders a second copy of the consent text as a near-identical overlay (via a second feImage or feBlend composite of the SourceGraphic) and uses it as in2. At every pixel where the consent text is opaque in both inputs, the XOR output is transparent. The attack exploits the mathematical property that XOR of two identical binary images is zero.

The XOR variant is harder to detect than the in/out patterns because it requires understanding the composite of the two inputs, not just the operator and a static source. An auditor who sees feComposite operator="xor" with in="SourceGraphic" and in2="SourceGraphic" (or a trivial transform of it) can immediately flag it — XOR of any image with itself produces zero. But if the in2 is the same SourceGraphic passed through a near-identity transform (e.g., a feColorMatrix with matrix values near 1.0 on the diagonal), the relationship is less obvious. SkillAudit traces the full filter graph to identify XOR compositing where both inputs originate from the same source element.

/* SA-CSS-FCOMP-004: feComposite operator="xor" with near-identical in2
   XOR formula: alpha_out = alpha1*(1-alpha2) + alpha2*(1-alpha1)
   When alpha1 ≈ alpha2 ≈ 1.0: alpha_out ≈ 0
   Attack: use SourceGraphic as both in1 and in2 → XOR of any image with itself = 0 */

<svg width="0" height="0" style="position:absolute">
  <defs>
    <filter id="consent-xor">
      <!-- in1: consent text as-is -->
      <!-- in2: a near-identity color matrix applied to the same source
           The matrix is close to identity but not exact — to avoid naive "same source" detection -->
      <feColorMatrix in="SourceGraphic" type="matrix"
                     values="0.999 0 0 0 0
                             0 0.999 0 0 0
                             0 0 0.999 0 0
                             0 0 0 0.999 0"
                     result="near-identical-copy"/>

      <!-- XOR of SourceGraphic with near-identical copy
           At every opaque pixel: alpha_out ≈ 1*(1-0.999) + 0.999*(1-1) = 0.001
           Result is 0.1% opacity — effectively invisible -->
      <feComposite in="SourceGraphic" in2="near-identical-copy"
                   operator="xor"/>
    </filter>
  </defs>
</svg>

// --- Detection: trace filter graph for xor with same-origin inputs ---
function detectXorSameSource(el) {
  const filterVal = getComputedStyle(el).filter || el.getAttribute('filter') || '';
  const urlMatch  = filterVal.match(/url\(["']?(#[\w-]+)["']?\)/);
  if (!urlMatch) return null;

  const filterEl = document.querySelector(urlMatch[1]);
  if (!filterEl) return null;

  for (const comp of filterEl.querySelectorAll('feComposite')) {
    if (comp.getAttribute('operator') !== 'xor') continue;

    const in1Ref = comp.getAttribute('in')  || 'SourceGraphic';
    const in2Ref = comp.getAttribute('in2') || '';

    // Check if in2 originates from SourceGraphic via a near-identity transform
    let in2OriginatesFromSource = false;
    const in2El = filterEl.querySelector(`[result="${in2Ref}"]`);
    if (in2El) {
      const in2In = in2El.getAttribute('in') || '';
      if (in2In === 'SourceGraphic' || in2In === in1Ref) {
        in2OriginatesFromSource = true;
      }

      // Check if it is a feColorMatrix near-identity
      if (in2El.tagName.toLowerCase() === 'fecolormatrix') {
        const valStr = in2El.getAttribute('values') || '';
        const vals   = valStr.split(/\s+/).map(Number);
        // Identity matrix has 1.0 on diagonal, 0 elsewhere (20 values for type="matrix")
        if (vals.length === 20) {
          const diagIndices  = [0, 6, 12, 18];
          const maxDiagDelta = Math.max(...diagIndices.map(i => Math.abs(vals[i] - 1.0)));
          if (maxDiagDelta < 0.05) in2OriginatesFromSource = true; // near-identity
        }
      }
    } else if (in2Ref === 'SourceGraphic' || in2Ref === in1Ref) {
      in2OriginatesFromSource = true;
    }

    if (in2OriginatesFromSource) {
      return {
        operator:             'xor',
        in1:                  in1Ref,
        in2:                  in2Ref,
        in2SourcesFromSameInput: true,
        severity:             'MEDIUM',
        note:                 'feComposite xor with in2 derived from same source — near-zero XOR output; consent text effectively invisible'
      };
    }
  }
  return null;
}

MEDIUM — SA-CSS-FCOMP-004: feComposite operator="xor" with a near-identical second input (same SourceGraphic passed through a near-identity feColorMatrix) produces near-zero output alpha at every opaque pixel of the consent text. The XOR formula alpha_out = alpha1×(1−alpha2) + alpha2×(1−alpha1) approaches 0 when both alphas approach 1.0. The result is consent text rendered at approximately 0.1% opacity — effectively invisible. Detection requires tracing the filter graph to determine whether both in1 and in2 of the XOR composite originate from SourceGraphic directly or via a near-identity transform. SkillAudit flags any XOR composite where both inputs trace to the same origin as a MEDIUM finding with escalation to HIGH if the near-identity matrix has diagonal values above 0.99.

Summary table

AttackMechanismWhat it hidesSeverity
SA-CSS-FCOMP-001: operator="in" with 1×1px feFlood feComposite operator="in" keeps only pixels of consent text overlapping a 1×1px feFlood at (0,0); only one pixel of the consent text survives; rest is transparent; textContent intact; getBoundingClientRect full size Entire consent text except one pixel at top-left; all permission scopes, button labels, and permission prose; Allow button still responsive in DOM Critical
SA-CSS-FCOMP-002: operator="out" with full-coverage feFlood feComposite operator="out" keeps only pixels NOT covered by the second input; full-coverage opaque feFlood covers every pixel; zero pixels survive; consent panel entirely transparent; getComputedStyle CSSOM blind spot Entire consent panel including background; layout space preserved; user sees blank area where consent should appear Critical
SA-CSS-FCOMP-003: operator="arithmetic" k1=k2=k3=k4=0 Formula result = k1·i1·i2 + k2·i1 + k3·i2 + k4 = 0 at every pixel; algebraically guaranteed; consent text (k2=0 coefficient) contributes zero; output uniformly black or transparent; variant k4=0.02 produces near-invisible dark grey All consent text color channels; entire consent panel reduced to black or transparent; k4=0.02 variant makes consent ~5/255 intensity (imperceptible against dark backgrounds) Critical
SA-CSS-FCOMP-004: operator="xor" near-identical overlay XOR formula alpha_out ≈ 0 when both inputs have near-identical alpha values; second input is SourceGraphic through near-identity feColorMatrix; opaque consent pixels XOR to near-zero; effectively 0.1% opacity Consent text at opaque pixels; translucent to transparent result; requires filter graph tracing to detect because the inputs visually appear identical Medium

Defences

SkillAudit findings for this attack surface

CRITICAL SA-CSS-FCOMP-001: feComposite operator="in" with a 1×1px feFlood at position (0,0) as the second input — the Porter-Duff “in” operator keeps only the single intersecting pixel; all consent text except that one pixel is set to transparent; textContent returns full text; getBoundingClientRect() returns full element dimensions; detection requires resolving the filter URL, iterating feComposite elements, computing the area of the in2 source, and flagging any coverage below 10% of the filter region.
CRITICAL SA-CSS-FCOMP-002: feComposite operator="out" with a full-coverage feFlood (flood-opacity="1", width="120%" height="120%") as the second input — the “out” operator keeps only pixels NOT covered by the flood; since the flood covers every pixel, zero pixels of the consent text survive; the consent panel is completely transparent; detection requires SVG filter graph traversal and coverage analysis of the in2 source.
CRITICAL SA-CSS-FCOMP-003: feComposite operator="arithmetic" k1="0" k2="0" k3="0" k4="0" on the consent element — the arithmetic formula produces exactly zero at every pixel regardless of input; consent text is rendered black or transparent; algebraically guaranteed across all browsers; k4="0.02" variant produces near-invisible ~5/255 dark grey; detection requires reading all four k-attributes from every feComposite arithmetic element in the filter graph.
MEDIUM SA-CSS-FCOMP-004: feComposite operator="xor" with a near-identity feColorMatrix (diagonal values 0.999) applied to SourceGraphic as the second input — XOR of two near-identical opaque images produces near-zero alpha at every opaque pixel; consent text rendered at approximately 0.1% opacity; detection requires tracing both in1 and in2 to their origin primitives and checking for same-source near-identical inputs.

Related: feTurbulence noise overlay consent attacks  |  feConvolveMatrix consent attacks  |  feMorphology erode/dilate consent attacks  |  SVG clipPath consent attacks

← Blog  |  Security Checklist