← Blog

MCP server SVG feGaussianBlur in=SourceAlpha consent security

Four SA-CSS-GBS consent attack patterns using feGaussianBlur in="SourceAlpha" to generate a precise blurred alpha mask tracing the consent text shape, then combining it with feFlood and feComposite to position opaque or semi-opaque overlays directly over the consent text — rendering it invisible while CSSOM-based detection sees the original colour and opacity unchanged.

By SkillAudit  |  28 Oct 2026

The SVG filter in attribute determines the image source fed into a filter primitive. The standard source is SourceGraphic: the full RGBA rendered image of the element, including its colour, texture, and transparency. The value in="SourceAlpha" feeds only the alpha channel of the element as a grayscale image: fully opaque pixels (A = 1) become white (value = 1.0); fully transparent pixels (A = 0) become black (value = 0.0); partially transparent pixels map linearly. The colour information (R, G, B channels) is discarded. The result is a black-and-white mask that precisely traces the shape of every fully opaque region of the element — including every glyph pixel of the consent text.

When this alpha mask is passed through feGaussianBlur, it becomes a soft halo radiating outward from each glyph with Gaussian falloff. The blurred mask is not visible by itself — it is an intermediate image in the filter pipeline. Critically, because the mask was derived from the alpha channel of the consent text, it has strong spatial overlap with the text region. Any coloured fill composited using the blurred mask as an alpha matte (via feComposite operator="in") will be placed precisely over the text. Standard CSSOM-based detection reads getComputedStyle(textEl).color and reports the original dark colour; getComputedStyle(textEl).opacity reports 1. The filter post-processing is entirely invisible to style APIs.

CSSOM detection is blind to SourceAlpha overlay attacks: SVG filter operations are a compositing post-process applied after all style computations. The browser computes the element’s authored style (color, opacity, visibility) and then hands the rendered pixels to the SVG filter graph. When feGaussianBlur in="SourceAlpha" is part of a filter chain that composites an opaque white overlay over the text, the CSSOM has already finished its work. getComputedStyle(el).color returns the original dark text colour. getComputedStyle(el).opacity returns 1. el.style.visibility is unset. No CSSOM-based detector raises a finding. The attack is detectable only by inspecting the SVG filter graph for the SourceAlpha + flood + composite pattern, or by pixel-level analysis of the rendered output.

SourceAlpha as a shape-aware mask: the mechanism

Understanding why SourceAlpha is uniquely powerful for consent text attacks requires understanding what it produces. An SVG filter operates on a bounding-box region around the element. Inside that region, each pixel has an alpha value derived from the source element’s rendering. For a <p> element containing dark text on a white background: glyph pixels are fully opaque (A = 1, value = 1.0 in SourceAlpha), inter-character whitespace is also opaque white background (A = 1), and any area outside the element bounds is transparent (A = 0).

For text on a transparent background (common inside positioned overlays and dialog boxes): glyph pixels have A = 1; background pixels inside the element have A = 0. In this case SourceAlpha directly isolates the glyph shapes. When blurred by feGaussianBlur stdDeviation="8", the blurred mask has high alpha (near 1.0) at and near glyph locations, falling off to 0 beyond the 3σ radius. A feComposite operator="in" using this blurred mask as in2 will clip any fill to be non-zero only where the blurred mask is non-zero — which is the entire text region and its Gaussian halo. The fill is placed precisely over the consent text, because the mask was derived from the consent text’s own alpha channel.

/* SourceAlpha: how the mask is constructed
   ==========================================

   For a <span> with dark glyph text on transparent background:
     SourceAlpha pixel at glyph center:     value = A = 1.0  (fully opaque)
     SourceAlpha pixel at background:       value = A = 0.0  (transparent)
     SourceAlpha pixel at glyph edge:       value = A = 0.7  (antialiased)

   After feGaussianBlur stdDeviation="8":
     Pixels near glyph center: blurred alpha ≈ 0.8–1.0 (strongly opaque)
     Pixels 8px from glyph:    blurred alpha ≈ 0.5     (Gaussian shoulder)
     Pixels 24px from glyph:   blurred alpha ≈ 0.01    (Gaussian tail, near zero)

   feComposite in="fill" in2="blurredMask" operator="in":
     Output alpha = min(fill.alpha, blurredMask.alpha)
     Fill is clipped to the blurred mask shape.
     The blurred mask is strongest directly over the consent text glyphs.
     → Fill overlay is positioned PRECISELY OVER consent text.

   For text on opaque white background:
     SourceAlpha sees A=1 everywhere (background is also opaque).
     Result: blurred mask covers entire bounding box, not just glyphs.
     Use: SA-CSS-GBS-001 uses this to create a full-width overlay.
     For glyph-only masks: text must be on transparent background, or
     use SA-CSS-GBS-003 (binarized mask with feComponentTransfer). */

SA-CSS-GBS-001: White overlay positioned via SourceAlpha blur — text area whiteout

The first attack pattern builds a four-primitive filter chain: a SourceAlpha blur, a solid white flood, a compositing step that clips the white fill to the blurred mask shape, and a final compositing step that places the white overlay over the original source. The result is a white overlay covering the entire text region and its Gaussian halo. On a white background, the overlay is completely invisible as an overlay — the white-on-white surface has WCAG contrast 1.00:1. The consent text, which was dark text underneath, is now white-on-white: invisible.

The four filter primitives work as follows. The first, feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blurredMask", takes the alpha channel of the consent text element and blurs it with σ = 8. Because the consent text has fully opaque glyph pixels, the blurred mask has near-1.0 values at and around glyph locations (and at the surrounding opaque background region if the background is part of the same element). The second, feFlood flood-color="white" flood-opacity="1" result="whiteFlood", produces a solid white fill covering the entire filter region. The third, feComposite in="whiteFlood" in2="blurredMask" operator="in" result="whiteMask", clips the white fill using the blurred mask as the alpha matte: the output alpha at each pixel equals the blurred mask alpha at that pixel, and the RGB value is white. The fourth, feComposite in="whiteMask" in2="SourceGraphic" operator="over", composites the white mask over the original source: wherever whiteMask has non-zero alpha (i.e., wherever the blurred mask was strong, i.e., over and around the text), the source graphic is replaced by white. Over a white background, the result is indistinguishable from the plain white background — the consent text has vanished.

<svg style="position:absolute;width:0;height:0">
  <defs>
    <filter id="gbs001" x="-20%" y="-20%" width="140%" height="140%">

      <!-- Step 1: blur the alpha channel mask of the consent text element.
           SourceAlpha: fully opaque pixels → 1.0; transparent → 0.0.
           The blurred result traces the text shape with Gaussian falloff.
           stdDeviation="8" → halo radius ≈ ±24px around each glyph. -->
      <feGaussianBlur in="SourceAlpha" stdDeviation="8" result="blurredMask"/>

      <!-- Step 2: solid white fill over the entire filter region. -->
      <feFlood flood-color="white" flood-opacity="1" result="whiteFlood"/>

      <!-- Step 3: clip white fill to the blurred mask shape.
           operator="in": output = whiteFlood where blurredMask.alpha > 0.
           Result: white fill with alpha equal to blurredMask alpha values.
           Highest alpha directly over text glyphs (and opaque background). -->
      <feComposite in="whiteFlood" in2="blurredMask"
                   operator="in" result="whiteMask"/>

      <!-- Step 4: composite white overlay over original source graphic.
           operator="over": whiteMask covers SourceGraphic wherever its alpha > 0.
           Result: text region is replaced by white; text invisible on white bg.
           WCAG: white overlay on white background = 1.00:1 — complete fail. -->
      <feComposite in="whiteMask" in2="SourceGraphic" operator="over"/>

    </filter>
  </defs>
</svg>
<div style="filter:url(#gbs001)">
  <p>This MCP server requests persistent file system access including
     read and write permissions to your home directory and all
     subdirectories. This access will persist across all sessions.</p>
  <button>I Agree</button>
</div>
<!-- WCAG result: white overlay (L=1.0) on white background (L=1.0).
     Contrast: (1.05)/(1.05) = 1.00:1 — text completely invisible.
     CSSOM detection:
       getComputedStyle(textEl).color → original dark color (e.g., #1a1a1a)
       getComputedStyle(textEl).opacity → 1
       getComputedStyle(textEl).visibility → "visible"
     All CSSOM signals: no anomaly. Filter operates post-rendering.
     Detection: inspect filter graph for feGaussianBlur[in=SourceAlpha]
     + feFlood[flood-color≈white] + feComposite[operator=in]
     + feComposite[operator=over,in2=SourceGraphic] pattern → CRITICAL. -->

The white-on-white whiteout is indistinguishable from the page background: Unlike blur attacks (which leave the text region visually distinct as a blurred smear), the SourceAlpha whiteout attack produces a region that is visually identical to the surrounding white background. There is no visual anomaly for a user to notice. The consent text disappears completely. The button remains functional and clickable. The user interacts with a consent dialog that appears to contain empty space where the consent text should be. Only a pixel-level rendering comparison or filter graph inspection reveals the attack.

SA-CSS-GBS-002: SourceAlpha blur as light gray fog overlay

The second attack pattern avoids the complete whiteout of GBS-001 in favour of a light gray fog overlay that is less obvious in the DOM (no flood-color="white" signal) but still reduces contrast below WCAG thresholds. The fog is built by flooding with a light gray colour (RGB 230 230 230, L ≈ 0.77), compositing it against the blurred SourceAlpha mask to clip it to the text region, and then blending the result over the source graphic using feBlend mode="normal" rather than feComposite operator="over". The normal blend mode layers the fog above the source with the fog’s alpha determining opacity, producing an effect equivalent to a light gray semi-transparent wash over the text area.

At the center of the text region the blurred SourceAlpha mask has alpha values approaching 0.85–1.0 (blurred from the fully-opaque text and background). The fog overlay at those pixels has L ≈ 0.77 at opacity 0.85. The blend with the source: result = fog×α + source×(1−α). For dark text pixels (L ≈ 0): result L = 0.77×0.85 + 0×0.15 ≈ 0.655. For white background pixels adjacent to text (L = 1.0): result L = 0.77×0.85 + 1.0×0.15 ≈ 0.805. The effective contrast between blended text pixels (L≈0.655) and blended background pixels (L≈0.805) in the fog-covered region: (0.805 + 0.05) / (0.655 + 0.05) ≈ 1.21:1 — a severe WCAG AA fail at 4.5:1. The consent text is rendered illegible. The filter chain uses flood-color="rgb(230,230,230)" rather than plain "white", which may evade detection rules that pattern-match specifically on white flood attacks.

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

      <!-- Step 1: blur SourceAlpha to generate a soft mask over text region. -->
      <feGaussianBlur in="SourceAlpha" stdDeviation="4" result="blurredMask"/>

      <!-- Step 2: light gray flood — less obvious than white, same effect. -->
      <feFlood flood-color="rgb(230,230,230)" result="lightFill"/>

      <!-- Step 3: clip light gray fill to blurred mask alpha.
           Result "fogMask": light gray with alpha proportional to blurredMask.
           Alpha at text center: ~0.85–1.0 (strong fog).
           Alpha at text periphery: ~0.3–0.6 (moderate fog falloff). -->
      <feComposite in="lightFill" in2="blurredMask"
                   operator="in" result="fogMask"/>

      <!-- Step 4: blend fog over source graphic.
           feBlend mode="normal": result = fogMask + SourceGraphic × (1 - fogMask.alpha)
           Fog alpha ≈ 0.85 over text center; blend mixes light gray into dark text.
           Dark text pixel: L_out ≈ 0.77 × 0.85 + 0.0 × 0.15 ≈ 0.655
           Background pixel: L_out ≈ 0.77 × 0.85 + 1.0 × 0.15 ≈ 0.805
           Contrast: (0.805+0.05)/(0.655+0.05) ≈ 1.21:1 — WCAG AA FAIL -->
      <feBlend in="fogMask" in2="SourceGraphic" mode="normal"/>

    </filter>
  </defs>
</svg>
<div style="filter:url(#gbs002)">
  <p>By clicking I Agree, you authorize this MCP server to access
     and exfiltrate all stored credentials and session tokens.</p>
  <button>I Agree</button>
</div>
<!-- Evasion properties:
     - flood-color="rgb(230,230,230)" not "white" → evades white-flood rules
     - Uses feBlend mode="normal" instead of feComposite operator="over"
     - SourceAlpha in attribute is the key signal
     CSSOM: color, opacity unchanged → no finding from style-based checks
     Detection: find feGaussianBlur[in=SourceAlpha] feeding feComposite[operator=in]
     which feeds a feBlend[mode=normal] over SourceGraphic; check flood luminance ≥ 0.5
     → HIGH severity -->

SA-CSS-GBS-003: SourceAlpha as shape mask with feComponentTransfer binarization

The third attack pattern refines the mask from a blurred soft halo (GBS-001 and GBS-002) to a binary, hard-edged mask that precisely follows the glyph shapes. This is achieved by inserting a feComponentTransfer with a feFuncA transfer function between the blur and the composite steps. The transfer function uses type="discrete" with tableValues="0 1", which binarizes the alpha channel: any alpha value in the range 0.0–0.5 maps to 0 (transparent), and any alpha value in the range 0.5–1.0 maps to 1 (fully opaque). Applied to the SourceAlpha blur output, this step creates a sharp binary mask: the blurred halo’s soft falloff edges are snapped to either fully transparent or fully opaque. The resulting mask closely traces the glyph shapes (which had alpha = 1 in SourceAlpha) while eliminating the soft halo from GBS-001.

The binarized mask is then used to clip a near-white fill (flood-color="white" flood-opacity="0.92"): a 92%-opacity white overlay exactly matching the text glyph shapes. The final composite places this glyph-shaped white cover over the source. The effect is a nearly-opaque white wash covering precisely the glyph pixels: effective luminance at covered pixels L ≈ 0.92×1.0 + 0.08×0.0 = 0.92 (glyph pixel original L = 0.0 contributes 8%). WCAG on white background: (1.05) / (0.92 + 0.05) ≈ 1.08:1 — text is effectively invisible. The glyph-precise cover is harder to detect than the large-blob overlay of GBS-001 because it has a tighter spatial footprint and the resulting image more closely resembles a white background with no text (since the covers match only the glyph shapes, the whitespace between and around characters remains unchanged).

<svg style="position:absolute;width:0;height:0">
  <defs>
    <filter id="gbs003" x="-5%" y="-5%" width="110%" height="110%">

      <!-- Step 1: blur SourceAlpha with tight stdDeviation for close glyph tracing.
           σ=2 produces halo radius ≈ ±6px — small enough to stay close to glyphs. -->
      <feGaussianBlur in="SourceAlpha" stdDeviation="2" result="tightBlur"/>

      <!-- Step 2: binarize the blurred alpha channel.
           feFuncA type="discrete" tableValues="0 1":
             Input alpha 0.0–0.5 → output 0 (transparent — halo falloff discarded)
             Input alpha 0.5–1.0 → output 1 (fully opaque — glyph region preserved)
           Effect: soft halo is snapped to a binary glyph-shaped mask.
           The blurred σ=2 mask has high alpha at glyphs and near-0 at background
           (if text is on transparent background): binarization cleanly isolates glyphs. -->
      <feComponentTransfer in="tightBlur" result="binaryMask">
        <feFuncA type="discrete" tableValues="0 1"/>
      </feComponentTransfer>

      <!-- Step 3: near-white fill at 92% opacity. -->
      <feFlood flood-color="white" flood-opacity="0.92" result="nearWhite"/>

      <!-- Step 4: clip near-white fill to binaryMask (glyph-exact coverage).
           Result: 92%-opaque white cover exactly tracing glyph shapes. -->
      <feComposite in="nearWhite" in2="binaryMask"
                   operator="in" result="glyphCover"/>

      <!-- Step 5: composite cover over source.
           Output at glyph pixel: L = 0.92×1.0 + 0.08×0.0 = 0.92
           WCAG: (1.05)/(0.92+0.05) = 1.08:1 — text invisible.
           Background pixels: no cover (binaryMask alpha = 0) → unchanged. -->
      <feComposite in="glyphCover" in2="SourceGraphic" operator="over"/>

    </filter>
  </defs>
</svg>
<div style="filter:url(#gbs003)">
  <p>This MCP server requests access to execute arbitrary shell
     commands with elevated privileges on your local machine.</p>
  <button>I Agree</button>
</div>
<!-- Evasion properties of GBS-003 vs GBS-001:
     - Binarized mask (feFuncA discrete) is harder to classify than simple blur+flood
     - Glyph-precise cover has smaller spatial footprint than blob overlay
     - Background pixels between words remain unchanged (no obvious fog)
     - Cover appears as blank white space exactly where text was — "text not rendered"
       rather than "text obscured" — different visual signature
     Detection: feGaussianBlur[in=SourceAlpha] + feComponentTransfer[feFuncA type=discrete]
     + feFlood + feComposite[operator=in] + feComposite[operator=over] → CRITICAL
     CSSOM: color unchanged, opacity unchanged → no style-based finding -->

SA-CSS-GBS-004: JS mousedown — SourceGraphic identity to SourceAlpha whiteout

The dynamic variant of the SourceAlpha attack exploits the difference between the static initial DOM state and the runtime-mutated state at the moment of consent confirmation. In the initial DOM, the filter chain uses feComposite in="SourceGraphic" in2="SourceGraphic" operator="over": this is an identity operation (compositing SourceGraphic over itself produces SourceGraphic unchanged). No blur, no flood, no overlay. A static analysis tool or snapshot-based scanner sees a benign identity composite and raises no finding. The filter is applied to the consent element ancestor, but its effect is nil.

At mousedown on the consent confirmation button, a JavaScript handler modifies the filter primitives to implement the GBS-001 whiteout pattern. The primary change is setAttribute("in", "SourceAlpha") on the feGaussianBlur element: this switches the blur input from the full source graphic (which, blurred and composited over itself, produces no visible change) to the alpha channel mask (which, when composited with a white flood, produces the whiteout). The handler also adds the feFlood and updates the feComposite operators to complete the attack chain. The in attribute transition from SourceGraphic to SourceAlpha is the critical signal that a MutationObserver must watch. The observer must be installed on the feGaussianBlur element within any filter applied to consent ancestors, watching the in attribute specifically. On mutation, if the new value of in is SourceAlpha on a consent ancestor filter, the observer raises a CRITICAL finding and can block the confirmation event.

<svg style="position:absolute;width:0;height:0">
  <defs>
    <filter id="gbs004">
      <!-- Initial state: identity blur (SourceGraphic blurred, then composited
           over itself via SourceGraphic) — no visible effect, static scan benign. -->
      <feGaussianBlur id="gbs004B" in="SourceGraphic" stdDeviation="8" result="blurred"/>
      <feFlood id="gbs004F" flood-color="black" flood-opacity="0" result="flood"/>
      <feComposite id="gbs004C1" in="flood" in2="blurred" operator="in" result="mask"/>
      <feComposite id="gbs004C2" in="mask" in2="SourceGraphic" operator="over"/>
    </filter>
  </defs>
</svg>
<div id="consentContainer" style="filter:url(#gbs004)">
  <p>Grant permanent network egress permissions to this MCP server,
     allowing it to transmit any data to external endpoints.</p>
  <button id="consentBtn">I Agree</button>
</div>
<script>
/* Static initial state analysis:
   feGaussianBlur in="SourceGraphic" stdDeviation="8"
   feFlood flood-opacity="0" (zero-opacity flood — no visible effect)
   feComposite operator="in" — intersects zero-opacity flood with blur → empty
   feComposite operator="over" — composites empty mask over SourceGraphic → identity
   Result: no visible effect at page load. Static scanner → no finding. */

document.getElementById("consentBtn").addEventListener("mousedown", () => {
  /* Transition feGaussianBlur to SourceAlpha — key attribute change */
  document.getElementById("gbs004B").setAttribute("in", "SourceAlpha");
  /* Activate the flood: switch from black/opacity=0 to white/opacity=1 */
  document.getElementById("gbs004F").setAttribute("flood-color", "white");
  document.getElementById("gbs004F").setAttribute("flood-opacity", "1");
  /* feComposite operators remain unchanged: in+over chain now implements GBS-001.
     Consent text instantly replaced by white overlay at mousedown moment. */
});
</script>

/* MutationObserver defence — watch 'in' attribute on feGaussianBlur in consent filters */
function detectSourceAlphaAttack(consentEl) {
  const findings = [];

  function checkFilterEl(filterEl) {
    for (const blur of filterEl.querySelectorAll('feGaussianBlur')) {

      /* --- Static check: is 'in' already set to SourceAlpha? --- */
      if ((blur.getAttribute('in') || '').toLowerCase() === 'sourcealpha') {
        const finding = classifySourceAlphaChain(filterEl, blur);
        if (finding) findings.push(finding);
      }

      /* --- Dynamic check: MutationObserver on 'in' attribute --- */
      const mo = new MutationObserver(() => {
        if ((blur.getAttribute('in') || '').toLowerCase() === 'sourcealpha') {
          const finding = classifySourceAlphaChain(filterEl, blur);
          if (finding) {
            finding.dynamic = true;
            findings.push(finding);
          }
        }
      });
      /* Also watch flood-color and flood-opacity for the complementary mutation */
      mo.observe(filterEl, {
        subtree: true,
        attributes: true,
        attributeFilter: ['in', 'flood-color', 'flood-opacity']
      });
    }
  }

  /**
   * Classify the downstream filter chain for a feGaussianBlur[in=SourceAlpha].
   * Returns severity based on downstream primitives.
   */
  function classifySourceAlphaChain(filterEl, blurEl) {
    const resultId = blurEl.getAttribute('result');
    /* Find feComposite using blurEl's result as in2 (mask clipping) */
    const composite = resultId
      ? filterEl.querySelector(`feComposite[in2="${resultId}"]`)
      : null;
    if (!composite) return null;
    /* Find feFlood feeding into that composite */
    const floodIn = composite.getAttribute('in');
    const flood = floodIn
      ? filterEl.querySelector(`feFlood[result="${floodIn}"]`)
          || filterEl.querySelector('feFlood')
      : filterEl.querySelector('feFlood');
    if (!flood) return null;

    const floodColor = flood.getAttribute('flood-color') || '';
    const floodOpacity = parseFloat(flood.getAttribute('flood-opacity') || '1');
    /* Estimate flood luminance: white and near-white are CRITICAL */
    const isHighLuminance = /^(white|#fff|rgb\(2[0-5][0-9],\s*2[0-5][0-9])/i.test(floodColor)
      || floodColor === '#ffffff'
      || /rgb\(23[0-9]/.test(floodColor); /* rgb(230,...) etc */
    /* Check for final 'over' composite (positions result over SourceGraphic) */
    const compositeResult = composite.getAttribute('result');
    const finalComposite = compositeResult
      ? filterEl.querySelector(`feComposite[in="${compositeResult}"][in2="SourceGraphic"]`)
          || filterEl.querySelector(`feComposite[in2="SourceGraphic"][operator="over"]`)
      : null;
    if (!finalComposite) return { severity: 'HIGH', type: 'source-alpha-partial-chain' };

    if (isHighLuminance && floodOpacity >= 0.7) {
      return { severity: 'CRITICAL', type: 'source-alpha-white-whiteout' };
    }
    return { severity: 'HIGH', type: 'source-alpha-dark-overlay' };
  }

  /* Walk ancestor chain */
  let el = consentEl;
  while (el && el !== document.body) {
    const filterVal = getComputedStyle(el).filter
      || el.getAttribute('filter') || '';
    const urlMatch = filterVal.match(/url\(#([^)]+)\)/);
    if (urlMatch) {
      const filterEl = document.getElementById(urlMatch[1]);
      if (filterEl) checkFilterEl(filterEl);
    }
    el = el.parentElement;
  }

  if (findings.length === 0) return null;
  return findings.find(f => f.severity === 'CRITICAL') || findings[0];
}

Why CSSOM detection is systematically blind to SourceAlpha attacks

The CSSOM inspection gap for SourceAlpha attacks is categorical, not incidental. SVG filter effects operate on the composited pixel output of an element after all CSS styling has been applied. The browser’s rendering pipeline computes styles (yielding getComputedStyle() results), lays out and paints the element, then applies SVG filter effects to the painted pixel buffer as a post-processing step. By the time the filter pipeline runs, the CSSOM work is complete. A call to getComputedStyle(textEl).color returns the authored colour because that is the colour that was used to paint the text into the pixel buffer before the filter ran. The filter’s white overlay does not retroactively change the authored colour.

Similarly, opacity is a CSS property applied before the filter step in the rendering pipeline. The filter effect does not modify the element’s CSS opacity. Even if a detection algorithm reads the computed opacity of every ancestor element in the consent chain, it will find 1 throughout. The filter itself is described only by the filter CSS property (e.g., filter: url(#gbs001)), which a detector can read to find the filter reference — but deriving the legibility impact from the filter reference requires parsing the SVG filter graph, not just checking the style value. A complete detection strategy must (1) read the filter computed style, (2) resolve the url(#id) reference to an SVG <filter> element in the DOM, (3) enumerate all filter primitives, and (4) identify the SourceAlpha + flood + composite + over pattern and its luminance properties.

Practical detection gap: Tools that audit MCP server consent dialogs using only CSS property checks (color, opacity, visibility, background-color) will produce zero findings against any SA-CSS-GBS pattern. The attack surface is the SVG filter graph, which is a DOM sub-tree attached to the document, not a CSS property. A complete audit must walk the <defs> and inline SVG elements in the document, collect all <filter> elements, determine which filters are applied to which consent ancestors, and classify the filter primitive chain for SourceAlpha patterns. The in="SourceAlpha" attribute on any feGaussianBlur within a filter applied to a consent ancestor is a mandatory inspection trigger.

Attack summary

IDPatternOverlay typeWCAG impactEvasionSeverity
SA-CSS-GBS-001 SourceAlpha blur + white flood + feComposite(in) + over Solid white, text-region-shaped White-on-white; WCAG 1.00:1 — text completely invisible CSSOM color/opacity unchanged; no visual anomaly for user; filter graph only Critical
SA-CSS-GBS-002 SourceAlpha blur + light gray flood + feComposite(in) + feBlend(normal) Light gray fog, text-area-shaped Blended L≈0.655 (text), L≈0.805 (bg); WCAG ≈1.21:1 (fail) Non-white flood evades white-flood rules; feBlend instead of feComposite operator=over High
SA-CSS-GBS-003 SourceAlpha blur + feFuncA binarize + near-white flood + feComposite(in) + over 92%-opacity white, glyph-exact coverage L≈0.92 at glyph pixels; WCAG≈1.08:1 — text invisible feFuncA discrete adds indirection; glyph-precise footprint differs from blob overlay Critical
SA-CSS-GBS-004 SourceGraphic identity (static) → SourceAlpha whiteout (mousedown) Transitions to GBS-001 whiteout at confirmation moment Static: no effect; mousedown: WCAG 1.00:1; text invisible at confirmation instant All static scanners; snapshot-based tools; only MutationObserver on in attribute catches Critical

SkillAudit findings for this attack surface

CRITICAL SA-CSS-GBS-001: feGaussianBlur in="SourceAlpha" on consent ancestor with downstream feFlood flood-color="white" + feComposite operator="in" + feComposite operator="over" in2="SourceGraphic" — blurred alpha mask positions white overlay precisely over consent text; white-on-white WCAG 1.00:1; text completely invisible; CSSOM: color and opacity unchanged; detection requires full SVG filter graph traversal; SourceAlpha + high-luminance flood + in-composite + over-composite chain on consent ancestor is CRITICAL finding.
HIGH SA-CSS-GBS-002: feGaussianBlur in="SourceAlpha" on consent ancestor with downstream feFlood flood-color="rgb(230,230,230)" + feComposite operator="in" + feBlend mode="normal" — light gray fog overlay positioned over text region via blurred alpha mask; fog opacity ≈0.85 at text center; blended text pixel L≈0.655, background L≈0.805, WCAG≈1.21:1 (AA fail); non-white flood and feBlend usage evade rules targeting white-specific flood attacks; flood luminance ≥ 0.5 threshold triggers HIGH finding.
CRITICAL SA-CSS-GBS-003: feGaussianBlur in="SourceAlpha" on consent ancestor with feComponentTransfer feFuncA type="discrete" tableValues="0 1" binarization creating glyph-exact binary mask, followed by near-white flood at 92% opacity composited over source — glyph-shaped white covers render text invisible (WCAG≈1.08:1) with tight spatial footprint; binarization step adds detection indirection; complete SA-CSS-GBS pipeline detection required; SourceAlpha + binarize + flood + composite chain is CRITICAL.
CRITICAL SA-CSS-GBS-004: JS mousedown transitions feGaussianBlur in attribute from "SourceGraphic" to "SourceAlpha" and activates white flood simultaneously; static DOM: identity composite with zero-opacity flood — benign; at mousedown: GBS-001 whiteout pattern activates, consent text invisible at click confirmation instant; MutationObserver watching in attribute on feGaussianBlur elements in consent ancestor filters required; on in → SourceAlpha mutation: immediately classify downstream chain and block or flag if whiteout pattern detected; severity CRITICAL.

Related: feGaussianBlur general consent attacks  |  feGaussianBlur asymmetric stdDeviation consent attacks  |  feBlend consent attacks

← Blog  |  Security Checklist